赞
踩
pip install django
# 已经进行了环境变量配置
django-admin.exe startproject 项目名称
mysite
- manage.py 【项目管理,启动项目、创建APP、数据管理】- 不修改,常用
- mysite
- __ init __.py
- settings.py 【项目配置文件】- 常常操作
- urls.py 【URL和函数的对应关系】- 常常操作
- asgi.py 【接收网络请求「异步式」】- 不修改
- wsgi.py 【接收网络请求「同步式」】- 不修改
1. APP 的例子
项目
- app,用户管理 【表结构、函数、HTML模板、CSS】
- app,订单管理 【表结构、函数、HTML模板、CSS】
- app,后台管理 【表结构、函数、HTML模板、CSS】
- app,网站 【表结构、函数、HTML模板、CSS】
- app,API 【表结构、函数、HTML模板、CSS】
- …
注意,我们开发比较简洁,用不到多 APP ,通常情况下,项目创建一个 APP 即可。
2. 创建 APP,在终端输入:
python manage.py startapp app01
3. APP 默认文件介绍
- app01
- __ init __.py
- admin.py【固定,不用动】 django默认提供了admin后台管理
- apps.py 【固定,不用动】 app启动类
- migrations 【固定,不用动】 数据库字段变更记录
- __ init __.py
- models.py 【重要】 对数据库进行操作
- tests.py【固定,不用动】 单元测试
- views.py 【重要】 函数
1. 注册 app 【setting.py】
2. 编写 URL 和视图函数的对应关系 【urls.py】
3. 编写视图函数 【views.py】
4. 启动 Django 项目
命令行启动
python manage.py runserver
1. templates 模板
2. 静态文件
在开发过程中,一般将 「图片、CSS、js」都会当作静态文件处理。
(1)在 app 目录下创建static文件夹
(2)引用静态文件
1. 本质:在 HTML 中写一些占位符,由数据对这些占位符进行替换和处理。
2. 案例:伪联通新闻中心
(1)在 urls.py 创建函数的对应关系
(2)在 views 中编写函数
(3)在 templates 的 news.html 编写模板语法
(4)页面展示
1.
2. 案例:用户登录
(1)发生如下错误,只需要在 form 表单中伪造跨站请求 {% csrf_token %} 。
(2)最终代码:
(3)效果展示
ORM
- 创建、修改、删除数据库中的表;(不用写SQL语句)【无法创建数据库】
- 操作表中的数据(不用写SQL语句)。
1. 安装第三方库
pip install mysqlclient==1.4.1
2. 创建数据库
net start mysql
mysql -u root -p
create database gx DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
3. django 连接数据库
在 settings.py 文件中进行配置和修改。
更改默认 DATABASES :
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # 引擎
'NAME': 'gx', # 数据库名字
'USER': 'root', # 用户名
'PASSWORD': '123456', # 密码
'HOST': '127.0.0.1', # 哪台机器安装了MySQL - 本机
'PORT': '3306',
}
}
4. django 创建表
(1)在 models.py 文件中:
等同于:
create table app01_userinfo(
id bigint auto_increment primary key, # django自动添加
name varchar(32),
password varchar(64),
age int
)
(2)在 pycharm 终端输入,注意, app 需要提前注册:
python manage.py makemigrations
python manage.py migrate
至此,表已经创建成功。
5. django 修改表
(1) 删除某个列:
python manage.py makemigrations
python manage.py migrate
(2)添加某个列:
- 在 python 代码中输入该列;
- 在 pycharm 终端输入:
python manage.py makemigrations
python manage.py migrate
(3)但是,对于 添加列 需要注意,由于已存在的列可能已有数据,所以新增列必须要指定对应的数据:
age = models.IntegerField(default=2)
data = models.IntegerField(null=True, blank=True)
总结:对表结构进行调整
- 在 models.py 文件中进行操作类;
- 输入命令:
python manage.py makemigrations
python manage.py migrate
6. 操作表中的数据
1. 展示用户列表
2. 添加用户
(1)在 urls.py 中添加 path:
(2)编写函数 info_add :
(3)编写 info_add.html ,注意要有 {% csrf_token %} ,使得跨站网址请求能够成功。
(4)效果展示
3. 删除用户
(1)在 urls.py 添加 path:
(2) 在 views.py 中编写相关代码:
(3)结果展示:(当然在 /info/list.html 中也做了修改)
在终端进入项目的存储路径,然后创建项目:
E:\PycharmProjects>django-admin.exe startproject day16
创建APP:
E:\PycharmProjects\day16>python manage.py startapp app01
注册 APP ,修改 setting.py :
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config',
]
models.py
from django.db import models # Create your models here. class Department(models.Model): """部门表""" title = models.CharField(verbose_name='标题', max_length=32) class UserInfo(models.Model): """员工表""" name = models.CharField(verbose_name="姓名", max_length=16) password = models.CharField(verbose_name="密码", max_length=64) age = models.IntegerField(verbose_name="年龄") account = models.DecimalField(verbose_name="账户余额", max_digits=10, decimal_places=2, default=0) create_time = models.DateTimeField(verbose_name="入职时间") # 1. 有约束 # - to,与哪张表关联 # - to_field,表中的哪一列关联 # 2. django自动 # - 写的 depart # - 实际生成 depart_id # 3. 部门表被删除 # 3.1 级联删除 depart = models.ForeignKey(to="Department", to_field="id", on_delete=models.CASCADE) # 3.2 置空 # depart = models.ForeignKey(to="Department", to_field="id", null=True, blank=True, on_delete=models.SET_NULL) gender_choices = ( (1, "男"), (2, "女"), ) gender = models.SmallIntegerField(verbose_name="性别", choices=gender_choices)
cmd 终端创建数据库
create database day16 DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
在 setting.py 中修改 DATABASES
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # 引擎
'NAME': 'day16', # 数据库名字
'USER': 'root', # 用户名
'PASSWORD': 'root', # 密码
'HOST': '127.0.0.1', # 哪台机器安装了MySQL - 本机
'PORT': '3306',
}
}
django 命令生成表
python manage.py makemigrations
python manage.py migrate
表结构创建成功:
depart_list.html
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.min.css' %}"> </head> <body> <nav class="navbar navbar-default"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">联通用户管理系统</a> </div> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li><a href="depart/list">部门管理</a></li> <li><a href="#">用户管理</a></li> </ul> <ul class="nav navbar-nav navbar-right"> <li><a href="#">登录</a></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">rice <span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="#">个人资料</a></li> <li><a href="#">用户信息</a></li> <li><a href="#">注销</a></li> <li role="separator" class="divider"></li> <li><a href="#">Separated link</a></li> </ul> </li> </ul> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav> <div> <div class="container"> <div style="margin-bottom: 10px"> <a class="btn btn-success" href="#">新建部门</a> </div> <div class="panel panel-default"> <div class="panel-heading"> <span class="glyphicon glyphicon-th-list" aria-hidden="true"></span> 部门列表 </div> <table class="table table-bordered"> <thead> <tr> <th>ID</th> <th>部门名称</th> <th>操作</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>销售部</td> <td> <a class="btn btn-primary btn-xs">编辑</a> <a class="btn btn-danger btn-xs">删除</a> </td> </tr> </tbody> </table> </div> </div> </div> </div> <script src="{% static 'jquery/jquery.min.js' %}"></script> <script src="{% static 'plugins/bootstrap-3.4.1/js/bootstrap.min.js' %}"></script> </body> </html>
views.py
from django.shortcuts import render
from app01 import models
# Create your views here.
def depart_list(request):
"""部门列表"""
# 去数据库中获取所有部门列表
# [对象, 对象, 对象]
queryset = models.Department.objects.all()
return render(request, 'depart_list.html', {'queryset': queryset})
depart_list.html
<tbody>
{% for obj in queryset %}
<tr>
<td>{{ obj.id }}</td>
<td>{{ obj.title }}</td>
<td>
<a class="btn btn-primary btn-xs">编辑</a>
<a class="btn btn-danger btn-xs">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
views.py
def depart_add(request):
"""添加部门"""
return render(request, 'depart_add.html')
depart_list.html
<div style="margin-bottom: 10px">
<a class="btn btn-success" href="/depart/add/" >新建部门</a>
</div>
depart_add.html
<div class="container"> <div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title">部门列表</h3> </div> <div class="panel-body"> <form> <div class="form-group"> <label>标题</label> <input type="text" class="form-control" placeholder="标题" name="title" /> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button type="submit" class="btn btn-primary">保 存</button> </div> </div> </form> </div> </div>
views.py
# 导入redirect
from django.shortcuts import render, redirect
def depart_add(request):
"""添加部门"""
if request.method == "GET":
return render(request, 'depart_add.html')
# 获取用户POST提交过来的数据
title = request.POST.get("title")
# 保存到数据库
models.Department.objects.create(title=title)
# 重定向到部门列表
return redirect('/depart/list')
depart_add.html
<div class="panel-body">
<form method="post">
{% csrf_token %}
<div class="form-group">
<label>标题</label>
<input type="text" class="form-control" placeholder="标题" name="title"/>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">提交</button>
</div>
</form>
views.py
def depart_delete(request):
"""删除部门"""
# 获取ID
nid = request.GET.get('nid')
# 删除
models.Department.objects.filter(id=nid).delete()
# 重定向回部门列表
return redirect("/depart/list/")
depart_list.html
<a class="btn btn-danger btn-xs" href="/depart/delete/?nid= {{ obj.id }}">删除</a>
urls.py
/< 正则表达式 >/
urlpatterns = [
# http://127.0.0.1:8000/depart/1/edit/
path('depart/<int:nid>/edit/', views.depart_edit),
]
views.py
def depart_edit(request, nid):
"""修改部门"""
if(request.method == "GET"):
# 根据nid 获取它的数据 [object,]
row_object = models.Department.objects.filter(id=nid).first()
return render(request, 'depart_edit.html', {"title": row_object.title})
# 获取用户提交的数据
title = request.POST.get("title")
# 更新数据
models.Department.objects.filter(id=nid).update(title=title)
# 重定向回部门列表界面
return redirect("/depart/list/")
depart_edit.html
<input type="text" class="form-control" placeholder="标题" name="title" value="{{ title }}"/>
layout.html
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.min.css' %}"> </head> <body> <div> {% block content %} {% endblock%} </div> <script src="{% static 'jquery/jquery.min.js' %}"></script> <script src="{% static 'plugins/bootstrap-3.4.1/js/bootstrap.min.js' %}"></script> </body> </html>
children.html
{% extends 'layout.html' %}
{% block content %}
<!--新增内容-->
{% endblock %}
当然, 也可以定义多个模板:
layout.html
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> {% block css %} {% endblock%} </head> <body> <div> {% block content %} {% endblock%} </div> {% block js %} {% endblock%} </body> </html>
children.html
{% extends 'layout.html' %}
{% block css %}
<link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.min.css' %}">
{% endblock%}
{% block content %}
<!--新增内容-->
{% endblock %}
{% block js %}
<script src="{% static 'jquery/jquery.min.js' %}"></script>
{% endblock%}
在 mysql 插入数据
insert into app01_userinfo(name, password, age, account, create_time, gender, depart_id) values("rice", "123", 23, 100.68, "2020-01-11", 2, 1);
insert into app01_userinfo(name, password, age, account, create_time, gender, depart_id) values("candy", "123", 26, 100.68, "2021-01-11", 1, 2);
insert into app01_userinfo(name, password, age, account, create_time, gender, depart_id) values("romance", "123", 25, 100.68, "2020-01-11", 2, 4);
urls.py
path('user/list/', views.user_list),
views.py
def user_list(request): """用户管理""" # 获取所有用户信息 queryset = models.UserInfo.objects.all() # python 写法 # for obj in queryset: # id = obj.id # name = obj.name # password = obj.password # age = obj.age # account = obj.account # create_time = obj.create_time.strftime("%Y-%m-%d") # gender = obj.get_gender_display() # title = obj.depart.title # 根据id自动去关联的表中获取哪一行的数据depart对象 return render(request, 'user_list.html', {"queryset": queryset})
user_list.html
<table class="table table-bordered"> <thead> <tr> <th>ID</th> <th>姓名</th> <th>密码</th> <th>年龄</th> <th>余额</th> <th>入职时间</th> <th>性别</th> <th>所属部门</th> <th>操作</th> </tr> </thead> <tbody> {% for obj in queryset %} <tr> <td>{{ obj.id }}</td> <td>{{ obj.name }}</td> <td>{{ obj.password }}</td> <td>{{ obj.age }}</td> <td>{{ obj.account }}</td> <td>{{ obj.create_time|date:"Y-m-d" }}</td> <td>{{ obj.get_gender_display }}</td> <td>{{ obj.depart.title }}</td> <td> <a class="btn btn-primary btn-xs" href="#">编辑</a> <a class="btn btn-danger btn-xs" href="#">删除</a> </td> </tr> {% endfor %} </tbody> </table>
效果展示
原始方法:比较复杂,因此最终不采用;
缺点
- 用户提交数据没有校验;
- 发生错误的时候,页面没有错误提示;
- 页面上,每一个字段都需要我们重新写一遍;
- 关联数据需要手动获取并通过循环才能展示在页面。
Django 组件
views.py
def user_add(request): """添加用户""" if request.method == "GET": context = { 'gender_choices': models.UserInfo.gender_choices, 'depart_list': models.Department.objects.all() } return render(request, 'user_add.html', context) # 获取用户提交的数据 user = request.POST.get('user') pwd = request.POST.get('pwd') age = request.POST.get('age') account = request.POST.get('ac') ctime = request.POST.get('ctime') gender = request.POST.get('gd') depart_id = request.POST.get('dp') # 添加到数据库中 models.UserInfo.objects.create(name=user, password=pwd, age=age, account=account, create_time=ctime, gender=gender, depart_id=depart_id) # 返回到用户列表界面 return redirect("/user/list")
user_add.html
<div class="container"> <div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title">用户列表</h3> </div> <div class="panel-body"> <form method="post"> {% csrf_token %} <div class="form-group"> <label>姓名</label> <input type="text" class="form-control" placeholder="姓名" name="user"/> </div> <div class="form-group"> <label>密码</label> <input type="text" class="form-control" placeholder="密码" name="pwd"/> </div> <div class="form-group"> <label>年龄</label> <input type="text" class="form-control" placeholder="年龄" name="age"/> </div> <div class="form-group"> <label>余额</label> <input type="text" class="form-control" placeholder="余额" name="ac"/> </div> <div class="form-group"> <label>入职时间</label> <input type="text" class="form-control" placeholder="入职时间" name="ctime"/> </div> <div class="form-group"> <label>性别</label> <select class="form-control" name="gd"> {% for gender in gender_choices %} <option value="{{ gender.0 }}">{{ gender.1 }}</option> {% endfor %} </select> </div> <div class="form-group"> <label>部门</label> <select class="form-control" name="dp"> {% for item in depart_list %} <option value="{{ item.id }}">{{ item.title }}</option> {% endfor %} </select> </div> <div class="form-group"> <button type="submit" class="btn btn-primary">提交</button> </div> </form> </div> </div> </div>
views.py
class MyForm(Form):
user = forms.CharField(wiget=forms.Input)
pwd = form.CharField(wiget=forms.Input)
age= form.CharField(wiget=forms.Input)
account = form.CharField(wiget=forms.Input)
create_time = form.CharField(wiget=forms.Input)
gender = form.CharField(wiget=forms.Input)
depart_id = form.CharField(wiget=forms.Input)
def user_add(request):
form = MyForm() # 实例化
return render(request, 'user_add.html', {"form": form})
user_add.html
form.user 相当于 input user
<form method="post">
{{ form.user }}
{{ form.pwd }}
<!--<input type="text" class="form-control" placeholder="姓名" name="user"/>-->
</form>
进一步优化,使用循环:
<form method="post">
{ for field in form %}
{{ field }}
{% endfor %}
</form>
models.py
class UserInfo(models.Model):
"""员工表"""
name = models.CharField(verbose_name="姓名", max_length=16)
password = models.CharField(verbose_name="密码", max_length=64)
age = models.IntegerField(verbose_name="年龄")
account = models.DecimalField(verbose_name="账户余额", max_digits=10, decimal_places=2, default=0)
create_time = models.DateTimeField(verbose_name="入职时间")
depart = models.ForeignKey(to="Department", to_field="id", on_delete=models.CASCADE)
gender_choices = (
(1, "男"),
(2, "女"),
)
gender = models.SmallIntegerField(verbose_name="性别", choices=gender_choices)
views.py
class MyForm(ModelForm):
class Meta:
model = UserInfo
fields = ["name", "password", "age"]
def user_add(request):
form = MyForm() # 实例化
return render(request, 'user_add.html', {"form": form})
user_add.html
form.user 相当于 input user
<form method="post">
{{ form.user }}
{{ form.pwd }}
<!--<input type="text" class="form-control" placeholder="姓名" name="user"/>-->
</form>
进一步优化,使用循环:
<form method="post">
{ for field in form %}
{{ field }}
{% endfor %}
</form>
views.py
from django import forms class UserModelForm(forms.ModelForm): class Meta: model = models.UserInfo fields = ["name", "password", "age", "account", "create_time", "depart", "gender"] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 循环找到所有插件,添加class="form-control" for name, field in self.fields.items(): field.widget.attrs = {"class": "form-control", "placeholder": field.label} def user_model_form_add(request): """添加用户 ModelForm版本""" form = UserModelForm() return render(request, 'user_model_form_add.html', {"form": form})
models.py
重写字段,保证 depart 作为用户列表的外键能够以字符形式显示。
class Department(models.Model):
"""部门表"""
title = models.CharField(verbose_name='标题', max_length=32)
def __str__(self):
return self.title
user_model_form_add.html
{% extends 'layout.html' %} {% block content %} <div class="container"> <div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title">用户列表</h3> </div> <div class="panel-body"> <form method="post"> {% csrf_token %} {% for field in form %} <div class="form-group"> <label>{{ field.label }}</label> {{ field }} </div> {% endfor %} <div class="form-group"> <button type="submit" class="btn btn-primary">提交</button> </div> </form> </div> </div> </div> {% endblock %}
效果图
views.py
重写字段 name ,用于数据验证(name 字段至少为 3 个字符),
name = forms.CharField(min_length=3, label="用户名")
:
class UserModelForm(forms.ModelForm):
# 重写字段
name = forms.CharField(min_length=3, label="用户名")
class Meta:
model = models.UserInfo
fields = ["name", "password", "age", "account", "create_time", "depart", "gender"]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 循环找到所有插件,添加class="form-control"
for name, field in self.fields.items():
field.widget.attrs = {"class": "form-control", "placeholder": field.label}
def user_model_form_add(request): """添加用户 ModelForm版本""" if request.method == "GET": form = UserModelForm() return render(request, 'user_model_form_add.html', {"form": form}) # 用户POST提交数据,数据校验 form = UserModelForm(data=request.POST) if form.is_valid(): # 如果数据合法,保存在数据库 form.save() return redirect('/user/list/') # 校验失败(在页面上显示错误信息) else: return render(request, 'user_model_form_add.html', {"form": form})
user_model_form_add.html
注意,网页自带的验证先取消 novalidate
,方便展示我们代码实现数据验证的效果。
field.errors.0
显示当前的第一个错误。
<form method="post" , novalidate>
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label>{{ field.label }}</label>
{{ field }}
<span style="color: red">{{ field.errors.0 }}</span>
</div>
{% endfor %}
<div class="form-group">
<button type="submit" class="btn btn-primary">提交</button>
</div>
</form>
效果图:
user_list.html
<a class="btn btn-primary btn-xs" href="/user/{{ obj.id }}/edit/">编辑</a>
urls.py
编辑用户需要获取用户的id
path('user/<int:nid>/edit/', views.user_edit),
views.py
def user_edit(request, nid):
"""编辑用户"""
# 根据id去数据库获取编辑行的数据
row_object = models.UserInfo.objects.filter(id=nid).first()
if request.method == "GET":
# 加入instance参数,原有的值会显示在输入框
form = UserModelForm(instance=row_object)
return render(request, 'user_edit.html', {'form': form})
form = UserModelForm(data=request.POST, instance=row_object)
if form.is_valid():
form.save()
return redirect('/user/list/')
return render(request, 'user_edit.html', {"form":form})
效果图
user_list.html
修改删除标签
<a class="btn btn-danger btn-xs" href="/user/{{ obj.id }}/delete/">删除</a>
urls.py
path('user/<int:nid>/delete/', views.user_delete)
views.py
def user_delete(request, nid):
models.UserInfo.objects.filter(id=nid).delete()
return redirect('/user/list/')
class PrettyNum(models.Model): """靓号表""" mobile = models.CharField(verbose_name="手机号", max_length=11) # 想要允许为空 null=True, blank=True price = models.IntegerField(verbose_name="价格", default=0) level_choices = ( (1, "1级"), (2, "2级"), (3, "3级"), (4, "4级"), ) level = models.SmallIntegerField(verbose_name="级别", choices=level_choices, default=1) status_choices = ( (1, "已占用"), (2, "未使用"), ) status = models.SmallIntegerField(verbose_name="状态", choices=status_choices, default=2)
layout.html
<li><a href="/pretty/list/">靓号管理</a></li>
urls.py
path('pretty/list/', views.pretty_list),
views.py
def pretty_list(request):
"""靓号管理"""
# order_by :降序
queryset = models.PrettyNum.objects.all().order_by("-level")
return render(request, 'pretty_list.html', {'queryset': queryset})
pretty.html
{% extends 'layout.html' %} {% block content %} <div class="container"> <div style="margin-bottom: 10px"> <a class="btn btn-success" href="#">新建靓号</a> </div> <div class="panel panel-default"> <div class="panel-heading"> <span class="glyphicon glyphicon-th-list" aria-hidden="true"></span> 用户列表 </div> <table class="table table-bordered"> <thead> <tr> <th>ID</th> <th>手机号</th> <th>价格</th> <th>级别</th> <th>状态</th> <th>操作</th> </tr> </thead> <tbody> {% for obj in queryset %} <tr> <td>{{ obj.id }}</td> <td>{{ obj.mobile }}</td> <td>{{ obj.price }}</td> <td>{{ obj.get_level_display }}</td> <td>{{ obj.get_status_display }}</td> <td> <a class="btn btn-primary btn-xs" href="#">编辑</a> <a class="btn btn-danger btn-xs" href="#">删除</a> </td> </tr> {% endfor %} </tbody> </table> </div> </div> {% endblock %}
/pretty/add/
urls.py
path('pretty/add/', views.pretty_add),
views.py
from django.core.validators import RegexValidator from django.core.validators import ValidationError class PrettyModelForm(forms.ModelForm): # 验证:方式1 字段+正则 # mobile = forms.CharField( # label="手机号", # validators=[RegexValidator(r'^1[3-9]\d{9}$', '手机号格式错误')], # ) class Meta: model = models.PrettyNum # fields = ["mobile", "price", "level", "status"] fields = "__all__" # exclude = ['level'] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for name, field in self.fields.items(): field.widget.attrs = {"class": "form-control", "placeholder": field.label} # 验证:方式2 钩子方法 def clean_mobile(self): txt_mobile = self.cleaned_data['mobile'] if len(txt_mobile) != 11: # 验证不通过 raise ValidationError("格式错误") # 验证通过 return txt_mobile
def pretty_add(request):
"""添加靓号"""
if request.method == "GET":
form = PrettyModelForm()
return render(request, 'pretty_add.html', {'form': form})
# POST提交数据,数据校验
form = PrettyModelForm(data=request.POST)
if form.is_valid():
form.save()
return redirect('/pretty/list/')
# 校验失败
else:
return render(request, 'pretty_add.html', {'form': form})
pretty_list.html
<a class="btn btn-success" href="/pretty/add/">新建靓号</a>
pretty_add.html
{% extends 'layout.html' %} {% block content %} <div class="container"> <div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title">靓号列表</h3> </div> <div class="panel-body"> <form method="post" novalidate> {% csrf_token %} {% for field in form %} <div class="form-group"> <label>{{ field.label }}</label> {{ field }} <span style="color: red">{{ field.errors.0 }}</span> </div> {% endfor %} <div class="form-group"> <button type="submit" class="btn btn-primary">提交</button> </div> </form> </div> </div> </div> {% endblock%}
效果图
数据校验的补充:数据库的手机号需要唯一
使用正则表达式判断 「手机号是否已经存在」
# True/False
exists = models.PrettyNum.objects.ilter(mobile="100000000").exists()
效果图:
views.py
class PrettyModelForm(forms.ModelForm): class Meta: model = models.PrettyNum fields = "__all__" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for name, field in self.fields.items(): field.widget.attrs = {"class": "form-control", "placeholder": field.label} # 验证:方式2 def clean_mobile(self): txt_mobile = self.cleaned_data['mobile'] # 手机号是否唯一的判断 exists = models.PrettyNum.objects.filter(mobile=txt_mobile).exists() if exists: raise ValidationError("手机号已存在") return txt_mobile
urls.py
path('pretty/<int:nid>/edit/', views.pretty_edit),
views.py
class PrettyEditModeleForm(forms.ModelForm): # disabled参数,该字段不可以编辑 # mobile = forms.CharField(disabled=True, label="手机号") class Meta: model = models.PrettyNum #fields = ["price", "level", "status"] fields = "__all__" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for name, field in self.fields.items(): field.widget.attrs = {"class": "form-control", "placeholder": field.label} # 验证:方式2 def clean_mobile(self): # 当前编辑行的id # self.instance.pk txt_mobile = self.cleaned_data['mobile'] # 手机号是否唯一的判断 exists = models.PrettyNum.objects.exclude(id=self.instance.pk).filter(mobile=txt_mobile).exists() if exists: raise ValidationError("手机号已存在") if len(txt_mobile) != 11: # 验证不通过 raise ValidationError("格式错误") # 验证通过 return txt_mobile
def pretty_edit(request, nid): """编辑靓号""" # 根据id去数据库获取编辑行的数据 row_object = models.PrettyNum.objects.filter(id=nid).first() if request.method == "GET": form = PrettyEditModeleForm(instance=row_object) return render(request, 'pretty_edit.html', {'form': form}) form = PrettyEditModeleForm(data=request.POST, instance=row_object) if form.is_valid(): form.save() return redirect('/pretty/list/') return render(request, 'pretty_edit.html', {'form': form})
pretty_list.html
<a class="btn btn-primary btn-xs" href="/pretty/{{ obj.id }}/edit/">编辑</a>
pretty_edit.html
{% extends 'layout.html' %} {% block content %} <div class="container"> <div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title">编辑靓号</h3> </div> <div class="panel-body"> <form method="post" novalidate> {% csrf_token %} {% for field in form %} <div class="form-group"> <label>{{ field.label }}</label> {{ field }} <span style="color: red">{{ field.errors.0 }}</span> </div> {% endfor %} <div class="form-group"> <button type="submit" class="btn btn-primary">提交</button> </div> </form> </div> </div> </div> {% endblock %}
效果图
数据校验:确保手机号的唯一性。
这和新建靓号中的校验不太一样。新建靓号的时候,对于新输入的手机号,是和数据库中的手机号相比判断的。
编辑靓号中,如果你更改的是手机号除外的信息,此时也没办法通过,因为你编辑后提交的手机号在数据库中已经存在了,因此在进行校验的时候需要排除自身的手机号再判断唯一性。
views.py
txt_mobile = self.cleaned_data['mobile']
# 当前编辑行的id
# self.instance.pk
exists = models.PrettyNum.objects.exclude(id=self.instance.pk).filter(mobile=txt_mobile).exists()
if exists:
raise ValidationError("手机号已存在")
效果图,编辑当前手机号的级别:
pretty_list.html
修改删除标签
<a class="btn btn-danger btn-xs" href="/pretty/{{ obj.id }}/delete/">删除</a>
urls.py
path('pretty/<int:nid>/delete/', views.pretty_delete),
views.py
def pretty_delete(request, nid):
models.PrettyNum.objects.filter(id=nid).delete()
return redirect('/pretty/list/')
数据库搜索相关知识补充
models.PrettyNum.objects.filter(mobile="12345678920", id=5)
# 以字典方式传入 必须要加 **
data_list = {"mobile": "12345678920", "id": 5}
models.PrettyNum.objects.filter(**data_list)
models.PrettyNum.objects.filter(id=5) # 等于5
models.PrettyNum.objects.filter(id__gt=5) # 大于5
models.PrettyNum.objects.filter(id__gte=5) # 大于等于5
models.PrettyNum.objects.filter(id__lt=5) # 小于5
models.PrettyNum.objects.filter(id__lte=5) # 小于等于5
models.PrettyNum.objects.filter(mobile="123") # 等于
models.PrettyNum.objects.filter(mobile__startswith="123") # 以123开头
models.PrettyNum.objects.filter(mobile__endswith="123") # 以123结尾
models.PrettyNum.objects.filter(mobile__contains="123") # 包含123
手机号搜索实现
本质:
在用户列表界面增加一个输入框,用来输入查询的手机号。
我们希望进行搜索时,条件会包含在网址里,即网址是 http://127.0.0.1:8000/pretty/list/?q=456
,那么需要通过 get
方式提交,即 <form method="get">
。那么后端会通过 request.GET.get
获取到 name="q"
的输入,将其放入字典 data_list
,作为数据库搜索的条件 filter(**data_list)
。
要想让输入框能够显示查询的手机号,需要把输入值 search_data
传入前端,放在<input>
标签的 value
字段。
views.py
def pretty_list(request):
"""靓号管理"""
data_list = {}
search_data = request.GET.get('q', "")
if search_data:
data_list["mobile__contains"] = search_data
queryset = models.PrettyNum.objects.filter(**data_list).order_by("-level")
return render(request, 'pretty_list.html', {'queryset': queryset, "search_data": search_data})
pretty_list.html
<div style="margin-bottom: 10px" class="clearfix"> <a class="btn btn-success" href="/pretty/add/">新建靓号</a> <div style="float: right; width:300px"> <form method="get"> <div class="input-group"> <input type="text" name="q" class="form-control" placeholder="Search for..." value="{{ search_data }}"> <span class="input-group-btn"> <button class="btn btn-default" type="submit"> <span class="glyphicon glyphicon-search" aria-hidden="true"></span> </button> </span> </div><!-- /input-group --> </form> </div> </div>
效果图:
pagination.py
""" 自定义分页组件 如果以后想要使用这个分页组件,需要做如下几件事: 在视图函数中: def pretty_list(request): # 1. 根据自己的情况去筛选数据 queryset = models.PrettyNum.objects.filter(**data_list).order_by("-level") # 2. 实例化分页对象 page_objects = Pagination(request, queryset) # 封装成组件 context = { 'search_data': search_data, 'queryset': page_objects.page_queryset, # 分完页的数据 "page_string": page_objects.html() # 生成的页码 } return render(request, 'pretty_list.html', context) # 在HTML中: {% for obj in queryset %} {{ obj.xx }} {% endfor %} <ul class="pagination"> {{ page_string }} </ul> """ from django.utils.safestring import mark_safe class Pagination(object): def __init__(self, request, queryset, page_size=10, page_param="page", plus=5): """ :param request: 请求的对象 :param queryset: 符合条件的数据 (根据这个数据给他进行分页处理) :param page_size: 每页显示多少条数据 :param page_param: 在URL中传递的获取分页的参数 例如:pretty/list/?page=1 :param plus: 显示当前页的前后几页(页码) """ import copy query_dict = copy.deepcopy(request.GET) query_dict.mutable = True self.query_dict = query_dict self.page_param = page_param page = int(request.GET.get(page_param, "1")) self.page = page self.page_size = page_size self.start = (page - 1) * page_size self.end = page * page_size self.page_queryset = queryset[self.start:self.end] total_count = queryset.count() # 总页码数 total_page_count, div = divmod(total_count, page_size) if div: total_page_count += 1 self.total_page_count = total_page_count self.plus = plus def html(self): # 计算当前页的前5页与后5页 if self.total_page_count <= 2 * self.plus + 1: start_page = 1 end_page = self.total_page_count + 1 else: if self.page <= self.plus: start_page = 1 end_page = 2 * self.plus + 1 else: if (self.page + self.plus) > self.total_page_count: start_page = self.total_page_count - 2 * self.plus end_page = self.total_page_count else: start_page = self.page - self.plus end_page = self.page + self.plus page_str_list = [] # 首页 self.query_dict.setlist(self.page_param, [1]) page_str_list.append('<li><a href="?{}">首页</a></li>'.format(self.query_dict.urlencode())) # 上一页 if self.page > 1: self.query_dict.setlist(self.page_param, [self.page - 1]) prev = '<li><a href="?{}">上一页</a></li>'.format(self.query_dict.urlencode()) else: self.query_dict.setlist(self.page_param, [1]) prev = '<li><a href="?{}">上一页</a></li>'.format(self.query_dict.urlencode()) page_str_list.append(prev) # 生成页码 for i in range(start_page, end_page): self.query_dict.setlist(self.page_param, [i]) if i == self.page: ele = '<li class="active"><a href="?{}">{}</a></li>'.format(self.query_dict.urlencode(), i) else: ele = '<li><a href="?{}">{}</a></li>'.format(self.query_dict.urlencode(), i) page_str_list.append(ele) # 下一页 if self.page < self.total_page_count: self.query_dict.setlist(self.page_param, [self.page + 1]) prev = '<li><a href="?{}">下一页</a></li>'.format(self.query_dict.urlencode()) else: self.query_dict.setlist(self.page_param, [self.total_page_count]) prev = '<li><a href="?{}">下一页</a></li>'.format(self.query_dict.urlencode()) page_str_list.append(prev) # 尾页 self.query_dict.setlist(self.page_param, [self.total_page_count]) page_str_list.append('<li><a href="?{}">尾页</a></li>'.format(self.query_dict.urlencode())) page_string = mark_safe("".join(page_str_list)) return page_string
views.py
from app01.utils.pagination import Pagination def pretty_list(request): """靓号管理""" data_list = {} search_data = request.GET.get('q', "") if search_data: data_list["mobile__contains"] = search_data queryset = models.PrettyNum.objects.filter(**data_list).order_by("-level") page_objects = Pagination(request, queryset) # 封装成组件 context = { 'search_data': search_data, 'queryset': page_objects.page_queryset, # 分完页的数据 "page_string": page_objects.html() # 生成的页码 } return render(request, 'pretty_list.html', context)
pretty_list.html
<div class="clearfix">
<ul class="pagination">
{{page_string}}
<li>
<form method="get">
<div class="input-group" style="width: 200px">
<input type="text" name="page" class="form-control" placeholder="页码">
<span class="input-group-btn">
<button class="btn btn-default" type="submit">跳转</button>
</span>
</div>
</form>
</li>
</ul>
</div>
视频3-10还有一半未看。
![在这里插入图片描述](https://img-blog.csdnimg.cn/38585175e498498b90b21bbcc6371528.png)
引入 bootstrap-datepicker
{% block css %}
<link rel="stylesheet" href="{% static '/plugins/bootstrap-datepicker-master/dist/css/bootstrap-datepicker.min.css' %}">
{% endblock %}
{% block js %}
<script src="{% static '/plugins/bootstrap-datepicker-master/dist/js/bootstrap-datepicker.min.js' %}"></script>
<script src="{% static '/plugins/bootstrap-datepicker-master/dist/locales/bootstrap-datepicker.zh-CN.min.js' %}"></script>
{% endblock %}
user_add.html
时间函数 ,其中 #dt
对应入职时间的ID
<input type="text" id="dt" class="form-control" placeholder="入职时间" name="ctime"/>
<script>
$(function() {
$('#dt').datepicker({
format: 'yyyy-mm-dd',
startDate: '0',
language: "zh-CN",
autoclose: true
})
})
</script>
user_model_form_add.html
时间函数 ,modelform中的ID默认为 ID_字段名
,即 #id_create_time
<script>
$(function() {
$('#id_create_time').datepicker({
format: 'yyyy-mm-dd',
startDate: '0',
language: "zh-CN",
autoclose: true
})
})
</script>
重复的内容可以集成为父类, 比如样式设置:
from django import forms class BootStrapModelForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 循环找到所有插件,添加class="form-control" for name, field in self.fields.items(): # 字段中有属性,保留原有属性,没有属性,才增加 if field.widget.attrs: field.widget.attrs["class"] = "form-control" field.widget.attrs["placeholder"] = field.label else: field.widget.attrs = { "class": "form-control", "placeholder": field.label }
之后,所有涉及到样式设置的 ModelForm 可以直接继承 BootStrap
,比如:
class UserModelForm(BootStrapModelForm):
由于已经实现了许多功能,原本的 views 函数显得复杂,此时可以新建一个 views 目录,将各个功能拆分成若干个 .py 文件。
注意,原本的 views.py
需要删除。
urls.py
也需要修改。
注意, models.py
函数不能像这样拆分。
参考资料:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。