Django-基础组件使用实战
前言
过时的技术,笔记是原来写的。但是由于其中有一些例子,所以想着还是上传一下。
非要用Python写后端,请移步使用Flask或者FastAPI。
安装Django
1 | pip install Django |
有一个项目生成器django-admin.exe
在Scripts
中,可以快速创建一个Django项目。
当前生成器的路径为:
1 | D:\anaconda\envs\django\Scripts\django-admin.exe |
创建Django项目
Django项目中有一个默认的文件和默认的文件夹
使用django-admin创建项目
使用命令创建
1 | D:\anaconda\envs\django\Scripts\django-admin.exe startproject mysite |
mysite为项目名称,项目结构为:
1 | mysite |
使用PyCharm创建项目
有奇怪的问题,创建不了Django项目,可以先创建一个新的env环境,然后在选择环境现有的conda环境,之后删除envs即可。
项目说明
使用命令行创建的项目是标准的
Pycharm创建的项目,在标准的基础上多加了点东西
创建了一个名为
templates
的文件夹,用来放模版文件在
settings.py
中:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [BASE_DIR / 'templates']
,
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]其中的
"DIRS": [BASE_DIR / 'templates']
把其中的内容删除即可。
默认项目的文件介绍
项目结构:
1 | mysite |
APP
1 | 项目 |
创建app
1 | python manage.py startapp app |
app结构
1 | app01 |
快速上手
确保APP已经注册【setting.py】
1
2
3
4
5
6
7
8
9
10INSTALLED_APPS = [
# 自己添加的app
"app01.apps.App01Config",
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
]编写url和视图函数的对应关系 【urls.py】 绑定相应的执行函数
在
urls.py
中1
2
3
4
5
6urlpatterns = [
# path("admin/", admin.site.urls),
# www.xxx.com/index/ -> views.index
path("index/", views.index),
path("user/add", views.user_add),
]编写视图函数,接收用户的请求 【views.py】
1
2
3
4
5
6
7
8
9
10
11
12
13
14from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
def index(request):
# return HttpResponse("Hello, Django!")
return render(request, "index.html")
def user_add(request):
return HttpResponse("添加用户成功!")启动Django项目
命令行启动
1
python manage.py runserver
Pycharm启动
选中项目文件夹点击运行即可。
添加/index查看结果
真正构建一个网页
加载模版文件
1 | def index(request): |
关于index.html
的查找顺序,先去本项目的根目录中的templates
文件夹,如果没有,那就根据注册的app的顺序,依次查找templates
文件夹中的HTML文件。
当然如果在setting.py中写入了项目文件的根目录中的templates
,那么就会优先去项目文件的根目录中找
1 | TEMPLATES = [ |
加载静态文件
在开发过程中,一般将:
- img
- CSS
- js
- plugins
都会当做静态文件处理。静态文件一定要放在static
文件夹中。推荐的目录结构:
1 | static |
推荐的引入方法:
1 | {#推荐使用的静态文件引入方法 是一种语言模版#} |
静态文件的相关的设置,在setting.py
:
1 | STATIC_URL = "/static/" |
模版语法
本质上:Django提供的,可以在HTML中写一些占位符,有数据对这些占位符进行替换和处理。
例如:
1 |
|
views.py中的
1 | def about(request): |
整个过程:
视图函数的render内部:
读取含有模板语法的HTML文件
内部进行渲染(模板语法执行并替换数据)
最终得到,只包含HTML标签的字符串
最后才将完成的字符串返回给用户浏览器
也就是说,用户不可能看到占位符中花括号的内容,都是Django渲染完成后才返回,可以在网页源代码中得到证实。
案例:伪联通新闻中心
1 | [ |
news.html
1 |
|
views.py
1 | def news(request): |
请求与响应
请求的方式
获取请求的数据
发送请求:
1
http://localhost:8000/something/?n1=456&n2=123
接受请求参数:
1
print(f"URL上的参数:{request.GET}")
重定向的原理:
案例:用户登录
这是Django的特殊机制之一,相当于保密机制
1 | <form action="/login/" method="post"> |
{% csrf_token %}
一定要加上这个信息,不然就会出现403,这条语句默认的返回值为:<QueryDict: {'csrfmiddlewaretoken': ['gAx9FmZWxIC5KM0vkDONo80C6o3Cosm0J2aWnjHlAq2hb5iAPx9fud8CVJJBcRHi'], 'username': ['gcnanmu'], 'password': ['qwe']}>
可以看到多了一个csrfmiddlewaretoken
,这是一个隐藏的输入框带来的,可以理解为一个加密的机制,检验请求是否正常,否则就返回403
数据库操作
Django的解决方案
MySQL数据库+pymysql
Django开发操作数据库更加简单,内部提供了ORM框架
可以使代码非常简洁。
安装第三方模块
1 | pip install mysqlclient |
可以使用pymysql
,但是新版的Django对pymysql支持不太好,因此推荐使用mysqlclient
比较好。
ORM框架
ORM可以帮我们做两件事:
- 创建、修改、删除数据库的表(不用写SQL语句,但无法创建数据库)
- 操作表中的数据(不用写SQL语句)
创建数据库
启动mysql服务
1
mysql -u root -p
查看当前的数据库
1
show database
创建数据库
1
create database django
Django连接数据库
更改settings.py的内容
1 | DATABASES = { |
Django操作表
- 创建表
- 删除表
- 修改表
创建与修改表
创建表,在models.py文件中:
1 | class UserInfo(models.Model): |
此时表并没有创建
执行命令
1 | python manage.py makemigrations |
注意你的app需要已经注册!!!!
发现有好多的表,原理是生成表的时候他会遍历整个项目的app,因此有一些默认的app也生成了相应的表
1 | INSTALLED_APPS = [ |
如果要创建多张表,那么我们需要创建多个类
1 | # 再创建几张表 |
然后再执行那两条命令即可。如果想要删除那个表或者表的选项,只需要注释掉相关代码执行命令即可。
但是如果想要往已存在的表中添加新的一行就会出现警告
有三个办法解决:
在warming中写入一个初始值
在添加的时候就设定初始值
1
size = models.IntegerField(default=2)
允许空值
1
2# 允许为空值
size = models.IntegerField(null=True, blank=True)
以后在开发总想要对表结构进行调整
在models.py文件中操作类即可
命令
1
2python manage.py makemigrations
python manage.py migrate
操作表中的数据
案例:用户管理
展示用户列表
添加用户
提交数据要么用表单,要么用Ajax,美化可以考虑Bootstrap
删除用户
主题:员工管理系统
创建app
新方法:
相当于运行了manage.py,这样运行的好处是会出现提示,而且不用自己写命令:
设计并创建数据库对象
拟设计部门表与员工表
设计时候会出现三个问题:
部门存储什么值?是存名称?还是ID?
- ID 数据库泛式,常见的开发都是这样做的。【节省存储开销】
- 存名称,特别大的公司,查询的次数会非常多,如果只存ID,需要查询多次【可以加速查找】
存储部门ID需不需要约束?
部门实际是有个数的限制的,所以需要约束,只能是部门表中已存在的ID
部门被删除了,员工还存在索引联系,怎么办?
- 直接删除相关员工信息 【级联删除】
- 将员工的部门ID设置为NULL 前提是数据允许为空【置空】
如何设置性别?
- 使用Boolen
- 使用SmallIntegerField类型,其中使用choices做约束
1
2
3
4
5gender_choice = {
(1, "男"),
(2, "女"),
}
gender = models.SmallIntegerField(verbose_name="性别", choices=gender_choice, default=1)
本次选择ID存储,部门ID采用关联的方法,保证只有ID来显示部门,当部门被删除后采用级联删除。
部门表结果为:
id | department |
---|---|
1 | 销售 |
2 | 运维 |
3 | 客服 |
用户表的设计为:
注意:
ID
是自动生成的depart
由于是与部门表关联的,会自动变为depart_id
id | name | password | age | account | create_time | depart_id | gender |
---|---|---|---|---|---|---|---|
1 | gcnanmu | 123456 | 23 | 200000 | 2020/01/01 | 1 | 1 |
2 | giao | 123456 | 25 | 0 | 2020/01/02 | 2 | 2 |
1 | from django.db import models |
生成数据库
创建数据库
1
2
3
4
5mysql -u root -p 123456
create database management_sys;
use management_sys;
show tables;创建表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16DATABASES = {
"default": {
# 引擎
"ENGINE": "django.db.backends.mysql",
# 数据库的名字
"NAME": "management_sys",
# 账户名
"USER": "root",
# 密码
"PASSWORD": "123456",
# 主机
"HOST": "localhost",
# 端口
"PORT": 3306,
}
}
生成结果:
静态文件管理
static文件夹
部门管理
需要使用最原始的方式来做。
Django中提供Form与ModelForm组件(方便)
部门列表
传递参数
1 | path("depart/<int:nid>/edit", views.depart_edit), |
可以将参数放在url中,后续可以直接在函数中获取参数
1 | def depart_edit(request, nid): |
模版的继承
每次都要复制一遍导航还是很麻烦的,所以可以使用继承的方法来减少代码的复杂程度
1 | {% load static %} |
{% block content %}{% endblock %}
实际为特殊的占位符
此时可以将depart_add.html
转化为如下形式
1 | {% extends "layout.html" %} |
{% extends "layout.html" %}
即为继承模版,必须写在文件的最开头
另外,这样写后续如果对导航进行编辑,就可以实现同步编辑,而不用每个html文件进行修改。
用户列表
创建数据最好直接使用Pycharm进行
要注意日期与性别的转化
新建用户
原始的方式:不采用,麻烦
× 问题1:如何进行数据的校验(是否为空号)
× 问题2:在页面上需要进行错误提示
× 问题3:如何数据库上有超级多的数据,那么页面上的每一个字段都需要我们自己重新写,很麻烦
× 问题4:如果有一些关联的数据(比如下拉框),需要自己手动取数据进行循环展示在页面中
Django组件
- Form组件(小简便)
- ModelForm组件(最简便)
原始方法:
1 | def user_add(request): |
html文件
1 | <div class="form-group"> |
初识Form
views.py
forms.CharField
要与models类定义的数据类型相同,有多少个字段就需要自己写上多少个1
2
3
4
5
6
7
8
9class MyFrom(Form):
user = forms.CharField(widget=forms.Input)
pwd = forms.CharField(widget=forms.Input)
email = forms.CharField(widget=forms.Input)
def user_add(request):
if request.method == "GET":
form = MyForm()
return render(request,"user_add.html",{"form":form})user_add.html
这样就不用自己手写html输入框了,这种方法只能显示输入框,不会显示提示,如果要显示的话,需要加上
form.name.label
,这种方法连接的是models.py
中模型的verbose_name
字段1
2
3
4
5
6<form method="POST">
{% csrf_token %}
{% for field in form %}
{{ field.label }} : {{ field }}<br>
{% endfor %}
</form>更简便的写法
1
2
3
4
5<form method="POST">
{% for field in form %}
{{ field.label }}:{{ field }}<br>
{% endfor %}
</form>
ModelForm(recommandation)
是针对数据库的操作,那么最好使用ModelFrom
快速上手
views.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14from django.forms import ModelForm
class MyForm(ModelForm):
class Meta:
# 支持自定义字段
xx = form.CharField("....")
model = UserInfo
# 如果返回全部可以使用
fields = "__all__"
fields = ["user","pwd","email","xx"]
def user_add(request):
if request.method == "GET":
form = MyForm()
return render(request,"user_add.html",{"form":form})user_add.html
1
2
3
4
5<form method="POST">
{% for field in form %}
{{ field.label }} : {{ field }}<br>
{% endfor %}
</form>
展示的结果是:
此时就会发现部门的部分有点问题,因为循环拿出的是对象,此时打印出的是对象本本身,如果要使打印出来的为特定的字符串,需要再对象中添加__str__
的魔术方法:
1 | class DepartMent(models.Model): |
这样打印出来的就是部门的名字,而不是对象本身:
但是这样并没有css样式,因为field是自动生成的html标签,这时候可以在定义modelform的类中这样写
1 | class UserModelForm(forms.ModelForm): |
因为添加的class都一样,这样写代码不够简便,可以改为这样
1 | class UserModelForm(forms.ModelForm): |
结果为:
数据的校验
这里只简单描述了如何校验空数据
1 | def user_modelform_add(request): |
需要注意的是,这边form.errors
它是一个列表,我们一般只需要取第一个就可以,而且需要将他显示在页面上。
1 | {% extends "layout.html" %} |
这样如果不输入数据就会有提示出现:
可以将提示改为中文,修改settings.py
中的LANGUAGE_CODE = "en-us"
改为LANGUAGE_CODE = "zh-hans"
如果需要自定义错误提示可以这样写:
1 | class UserModelForm(forms.ModelForm): |
编辑员工
我们的创建时间一般不需要精确到分钟,只需要到日就行
因此我们需要将models.py
中的create_time
字段修改为
1 | create_time = models.DateField(verbose_name="创建时间") |
如果要单独插入数据
1 | def user_edit(request, nid): |
主题:靓号管理
设计表结构
ID | moblile | price | level | status |
---|---|---|---|---|
1 | class PhoneNumber(models.Model): |
初始状态为:
新建靓号
- 新建连接
- 在views.py中定义操作函数
- 新建HTML文件,继承母版
- 使用modelform显示数据
手机号不允许重复
1 | def clean_mobile(self): |
编辑与删除靓号
编辑信息新建一个modelform来实现不一样的功能
不允许用户编辑手机号
手机号不允许重复
1
2
3
4
5
6
7
8
9# 排除自己以外,其他的数据是否重复?
# 可以使用self.instance获取当前编辑信息的id
def clean_mobile(self):
mobile_ = self.cleaned_data["mobile"]
exits = models.PhoneNumber.objects.exclude(id=self.instance).filter(mobile=mobile_).exists()
if exits:
raise forms.ValidationError("手机号已存在")
return mobile_
靓号搜索
按照索引
在数据库中查询
1 | models.PhoneModileForm.objects.filter(mobile="19999991",id="123") |
按照数字搜索
1 | # ID大于12 |
按照字符串搜索
1 | models.PhoneModileForm.objects.filter(mobile=12) |
上述的方法都可以使用字典作为条件,这种方法可以使用多个筛选条件。
分页展示
因为queryset实质是一个列表,所以可以使用切片来展示数据
1 | # 第一页 |
使用page
作为参数,这时候在html中生车工结构已经不合适了,需要在Python中生成在传入
分页组件的安装
价格分页系统直接分装为一个组件,需要注意的是,如果封装起来需要把跳转页面的代码也写入
1 | from django.shortcuts import redirect |
修改后的html
1 | from django.shortcuts import render, redirect |
以后哪里用分页就放到哪里用就好,但出现一个小问题,搜索的时候需要保留搜索的条件
1 | http://localhost:8000/phone/list?q=666 |
时间插件
1 | {% load static %} |
需要多引入东西,所以需要多写入几个block{% block js %}{% endblock %}
,{% block css %}{% endblock %}
需要引入以下依赖
1 | {% block css %} |
1 | <script> |
这个js的作用是找到id为dt的标签,然后作用在标签上。
所以在user_add.html
中要这样添加
1 | {% extends "layout.html" %} |
但是对于modelform_add.html
这个模版,因为是Django生成的,Django帮我们自己生成了相应的id,点击检查可以看到
所以只需要将id部分改为id_create_time
1 | <script> |
效果:
ModelForm与BootStrap
ModelForm可以帮助我们生成HTML标签
1
2
3
4
5class PhoneModelForm(ModelForm):
class Meta:
model = models.PhoneNumber
fields = ["mobile", "price", "level", "status"]
form = PhoneModelForm()1
2{{ form.name }} 普通的输入框
{{ form.age }} 普通的输入框定义插件 两种插件
1
2
3
4
5
6
7
8
9class PhoneModelForm(ModelForm):
class Meta:
model = models.PhoneNumber
fields = ["mobile", "price", "level", "status"]
widget = {
"name":forms.TextInput(attrs={"class":"form-control"}),
"password":forms.PasswordInput(attrs={"class":"form-control"}),
"age":forms.TextInput(attrs={"class":"form-control"})
}1
2
3
4
5
6
7
8
9class PhoneModelForm(ModelForm):
name = forms.TextInput(
min_length=3,
label="用户名",
widge=forms.TextInput(attrs={"class":"form-control"})
)
class Meta:
model = models.PhoneNumber
fields = ["mobile", "price", "level", "status"]1
2{{ form.name }} BootStrap的输入框
{{ form.age }} BootStrap的输入框重新定义的init方法,批量设置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class PhoneModelForm(forms.ModelForm):
class Meta:
model = models.PhoneNumber
fields = "__all__"
# fields = ["mobile", "price", "level", "status"]
# 排除哪个字段
# exclude = ["status"]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 循环Modelform中的所有字段,在每个字段设置插件
for name, item in self.fields.items():
item.widget.attrs = {
"class": "form-control",
"placeholder": "请输入%s" % (item.label,)
}以上方法会导致原有的属性被覆盖,可以优化如下:
1
2
3
4
5
6
7
8def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for name, item in self.fields.items():
if item.widget.attrs:
item.widget.attrs["class"] = "form-control"
item.widget.attrs["placeholder"] = "请输入%s" % (item.label,)
else:
item.widget.attrs = {"class": "form-control", "placeholder": "请输入%s" % (item.label,)}定义一个类进行继承
1
2
3
4
5
6
7
8
9
10class BootStrapModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 循环Modelform中的所有字段,在每个字段设置插件
for name, item in self.fields.items():
item.widget.attrs = {
"class": "form-control",
"placeholder": "请输入%s" % (item.label,)
}然后就可以将其他
forms.ModelForm
改为继承BootStrapModelForm
替换:
导入文件
1
from management_sys.utils.BootstrapModel import BootStrapModelForm
将其他的init方法删除,保留钩子方法
将所有ModelForm全写入一个py文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55from django import forms
from management_sys import models
from management_sys.utils.BootstrapModel import BootStrapModelForm
class UserModelForm(BootStrapModelForm):
# 自定义字段
name = forms.CharField(min_length=2, label="姓名",
error_messages={"required": "不能为空", "min_length": "长度不能小于2"})
# 如果要使用正则表达式验证,可以使用validators
password = forms.CharField(min_length=6, label="密码",
error_messages={"required": "不能为空", "min_length": "长度不能小于6"})
age = forms.IntegerField(max_value=100, label="年龄",
error_messages={"required": "不能为空", "max_value": "不能大于100"})
class Meta:
model = models.UserInfo
fields = "__all__"
class PhoneModelForm(BootStrapModelForm):
class Meta:
model = models.PhoneNumber
fields = "__all__"
# 方式二:钩子方法
def clean_mobile(self):
mobile_ = self.cleaned_data["mobile"]
exits = models.PhoneNumber.objects.filter(mobile=mobile_).exists()
if exits:
raise forms.ValidationError("手机号已存在")
# 验证不通过
if len(mobile_) != 11:
raise forms.ValidationError("手机号长度不对")
for i in mobile_:
if i not in "0123456789":
raise forms.ValidationError("手机号格式错误,必须全是数字")
return mobile_
class PhoneEditModelForm(BootStrapModelForm):
mobile = forms.CharField(disabled=True, label="手机号")
class Meta:
model = models.PhoneNumber
# 不让用户修改手机号
fields = ["mobile", "price", "level", "status"]
def clean_mobile(self):
mobile_ = self.cleaned_data["mobile"]
exits = models.PhoneNumber.objects.exclude(id=self.instance).filter(mobile=mobile_).exists()
if exits:
raise forms.ValidationError("手机号已存在")
return mobile_在views.py中修改
1
from management_sys.utils.forms import UserModelForm, PhoneModelForm, PhoneEditModelForm
可以将视图函数进行划分
可以将视图函数按照业务功能划分,单独创建一个views文件夹,将不同的业务划分到不同的py文件中
depart.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35from django import forms
from management_sys import models
from django.shortcuts import render, redirect
def depart_list(request):
data = models.DepartMent.objects.all()
return render(request, "depart_list.html", {"depart_list": data})
def depart_add(request):
if request.method == "GET":
return render(request, "depart_add.html")
title = request.POST.get("department")
models.DepartMent.objects.create(title=title)
return redirect("/depart/list")
def depart_delete(request):
data = request.GET.get("nid")
models.DepartMent.objects.filter(id=data).delete()
return redirect("/depart/list")
def depart_edit(request, nid):
if request.method == "GET":
# 返回值是一个列表
title = models.DepartMent.objects.filter(id=nid).first().title
return render(request, "depart_edit.html", {"title": title})
temp = request.POST.get("department")
# print(temp)
# 多个字段更新 在每个字段后面加上逗号
models.DepartMent.objects.filter(id=nid).update(title=temp, )
return redirect("/depart/list")user.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112from management_sys import models
from django.shortcuts import render, redirect
from management_sys.utils.forms import UserModelForm
def user_list(request):
data = models.UserInfo.objects.all()
# 这里是通过Django的Python代码来实现的
# for i in data:
# # 将datetime 类型转化为字符串类型
# # i.create_time = dt.strftime("%Y-%m-%d %H:%M:%S")
# i.create_time = dt.strftime("%Y-%m-%d")
# # 可以直接通过get_字段名_display()获取choices的值
# i.gender = i.get_gender_display()
# # 通过部门id获得部门名称 会将id自动转化名称
# i.depart_id = i.depart.title
return render(request, "user_list.html", {"user_list": data})
def user_add(request):
if request.method == "GET":
data = {
"gender_choices": models.UserInfo.gender_choice,
"depart_list": models.DepartMent.objects.all()
}
return render(request, "user_add.html", data)
username = request.POST.get("name")
password = request.POST.get("password")
age = request.POST.get("age")
account = request.POST.get("account")
create_time = request.POST.get("create_time")
depart = request.POST.get("depart")
gender = request.POST.get("gender")
print(username, password, age, gender, depart, account, create_time)
models.UserInfo.objects.create(name=username, password=password, gender=gender, depart_id=depart, age=age,
account=account, create_time=create_time)
return redirect("/user/list")
# class UserModelForm(BootStrapModelForm):
# # 自定义字段
# name = forms.CharField(min_length=2, label="姓名",
# error_messages={"required": "不能为空", "min_length": "长度不能小于2"})
# # 如果要使用正则表达式验证,可以使用validators
# password = forms.CharField(min_length=6, label="密码",
# error_messages={"required": "不能为空", "min_length": "长度不能小于6"})
# age = forms.IntegerField(max_value=100, label="年龄",
# error_messages={"required": "不能为空", "max_value": "不能大于100"})
#
# class Meta:
# model = models.UserInfo
# fields = "__all__"
# # depart 不要写成 depart_id 它可以自动找到命名
# # fields = ["name", "password", "age", "account", "depart", "gender","create_time"]
# # widgets = {
# # "name": forms.TextInput(attrs={"class": "form-control"}),
# # "password": forms.TextInput(attrs={"class": "form-control"}),
# # "age": forms.TextInput(attrs={"class": "form-control"}),
# # "account": forms.TextInput(attrs={"class": "form-control"}),
# # "depart": forms.Select(attrs={"class": "form-control"}),
# # "gender": forms.Select(attrs={"class": "form-control"}),
# # "create_time": forms.TextInput(attrs={"class": "form-control"}),
# # }
#
# # def __init__(self, *args, **kwargs):
# # super().__init__(*args, **kwargs)
# # for name, item in self.fields.items():
# # # item.widget.attrs.update({"class": "form-control", "placeholder": "请输入%s" % (item.label,)})
# # item.widget.attrs = {"class": "form-control", "placeholder": "请输入%s" % (item.label,)}
def user_modelform_add(request):
if request.method == "GET":
form = UserModelForm()
return render(request, "modelform_add.html", {"form": form})
form = UserModelForm(data=request.POST)
if form.is_valid():
print(form.cleaned_data)
form.save()
return redirect("/user/list")
else:
print(form.errors)
return render(request, "modelform_add.html", {"form": form})
def user_edit(request, nid):
instance_data = models.UserInfo.objects.filter(id=nid).first()
if request.method == "GET":
form = UserModelForm(instance=instance_data)
return render(request, "user_edit.html", {"form": form})
# 更新数据
form = UserModelForm(data=request.POST, instance=instance_data)
if form.is_valid():
print(form.cleaned_data)
# 如果想保存用户输入以外的其他值,可以使用以下方法
# form.instance.depart_id = request.POST.get("depart")
form.save()
return redirect("/user/list")
else:
print(form.errors)
return render(request, "user_edit.html", {"form": form})
def user_delete(request, nid):
models.UserInfo.objects.filter(id=nid).delete()
return redirect("/user/list")phone.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137from django.shortcuts import render, redirect
from management_sys import models
from management_sys.utils.forms import PhoneModelForm, PhoneEditModelForm
from django.utils.safestring import mark_safe
def phone_list(request):
data = {}
# models.PhoneNumber.objects.filter(id=1)
search_ = request.GET.get("q")
# 传过来的值可能为空
if search_:
data["mobile__contains"] = search_
# return render(request, "phone_list.html", {"data": data})
# 默认为第一页 不写有问题
page = request.GET.get("page", 1)
page = int(page)
# 每页的数据大小
page_size = 10
# 根据要访问的页码计算出起止位置
start = (page - 1) * page_size
end = page * page_size
# 可以进行排序 -level 表示按照级别降序
# data = models.PhoneNumber.objects.all().order_by("-level")
data = models.PhoneNumber.objects.filter(**data).order_by("-level")[start:end]
# 数据总数
total_count = models.PhoneNumber.objects.all().count()
# 总页数
page_total, div = divmod(total_count, page_size)
if div > 0:
page_total += 1
# 计算出显示当前页码的前五页,后五页
plus = 5
# 数据库的数据较少 页码全部显示
if page_total < 2 * plus + 1:
start_page = 1
end_page = page_total + 1
else:
# 数据库的数据较多
if page <= plus:
start_page = 1
end_page = 2 * plus + 1
elif page > page_total - plus:
start_page = page_total - 2 * plus
end_page = page_total + 1
else:
start_page = page - plus
end_page = page + plus + 1
# 生成页码
page_data = []
for i in range(start_page, end_page):
if i == page:
hl = '<li class="active"><a href="?page={}">{} <span class="sr-only">(current)</span></a></li>'.format(i, i)
else:
hl = '<li><a href="?page={}">{} <span class="sr-only">(current)</span></a></li>'.format(i, i)
page_data.append(hl)
# 首页 末页
shou_page = '<li><a href="?page={}">首页</a></li>'.format(1)
wei_page = '<li><a href="?page={}">末页</a></li>'.format(page_total)
# 上一页 下一页
prev_page = page - 1
next_page = page + 1
if prev_page > 1:
hl = '<li><a href="?page={}" aria-label="Previous"><span aria-hidden="true">«</span></a></li>'.format(
prev_page)
else:
hl = '<li class="disabled" disabled="True"><a href="?page={}" aria-label="Previous"><span aria-hidden="true">«</span></a></li>'.format(
1)
page_data.insert(0, hl)
if next_page <= page_total:
hl = '<li><a href="?page={}" aria-label="Next"><span aria-hidden="true">»</span></a></li>'.format(
next_page)
else:
hl = '<li class="disabled" disabled="True"><a href="?page={}" aria-label="Next"><span aria-hidden="true">»</span></a></li>'.format(
page_total)
page_data.append(hl)
page_data.insert(0, shou_page)
page_data.append(wei_page)
# 跳转页面
jump_page = request.GET.get("jump")
# jump_page = int(jump_page)
if jump_page is not None:
jump_page = int(jump_page)
if jump_page <= 1:
jump_page = 1
elif jump_page > page_total:
jump_page = page_total
return redirect("/phone/list?page=%s" % (jump_page,))
page_data = mark_safe("".join(page_data))
return render(request, "phone_list.html",
{"data": data, "search": search_, "page_data": page_data, "current_page": page})
def phone_add(request):
if request.method == "GET":
form = PhoneModelForm()
return render(request, "phone_add.html", {"form": form})
form = PhoneModelForm(data=request.POST)
if form.is_valid():
form.save()
return redirect("/phone/list")
else:
print(form.errors)
return render(request, "phone_add.html", {"form": form})
def phone_edit(request, nid):
instance_data = models.PhoneNumber.objects.filter(id=nid).first()
if request.method == "GET":
form = PhoneEditModelForm(instance=instance_data)
return render(request, "phone_edit.html", {"form": form})
form = PhoneEditModelForm(data=request.POST, instance=instance_data)
if form.is_valid():
form.save()
return redirect("/phone/list")
else:
print(form.errors)
return render(request, "phone_edit.html", {"form": form})
def phone_delete(request, nid):
models.PhoneNumber.objects.filter(id=nid).delete()
return redirect("/phone/list")需要导入的话需要把
views.py
给删除然后在
url.py
中导入1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25from django.urls import path
from management_sys.views import depart, user, phone
urlpatterns = [
# path("admin/", admin.site.urls),
# 部门管理
path("index/", depart.index),
path("depart/list", depart.depart_list),
path("depart/add", depart.depart_add),
path("depart/delete", depart.depart_delete),
path("depart/<int:nid>/edit", depart.depart_edit),
# 用户管理
path("user/list", user.user_list),
path("user/add", user.user_add),
path("user/modelformadd", user.user_modelform_add),
path("user/<int:nid>/edit", user.user_edit),
path("user/<int:nid>/delete", user.user_delete),
# 靓号管理
path("phone/list", phone.phone_list),
path("phone/add", phone.phone_add),
path("phone/<int:nid>/edit", phone.phone_edit),
path("phone/<int:nid>/delete", phone.phone_delete),
]给views.py存个档
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358from django import forms
from django.shortcuts import render, redirect
from management_sys import models
from datetime import datetime
from django.core.validators import RegexValidator
# 设置安全信任
from django.utils.safestring import mark_safe
from management_sys.utils.BootstrapModel import BootStrapModelForm
from management_sys.utils.forms import UserModelForm, PhoneModelForm, PhoneEditModelForm
# Create your views here.
def index(request):
return render(request, "index.html")
def depart_list(request):
data = models.DepartMent.objects.all()
return render(request, "depart_list.html", {"depart_list": data})
def depart_add(request):
if request.method == "GET":
return render(request, "depart_add.html")
title = request.POST.get("department")
models.DepartMent.objects.create(title=title)
return redirect("/depart/list")
def depart_delete(request):
data = request.GET.get("nid")
models.DepartMent.objects.filter(id=data).delete()
return redirect("/depart/list")
def depart_edit(request, nid):
if request.method == "GET":
# 返回值是一个列表
title = models.DepartMent.objects.filter(id=nid).first().title
return render(request, "depart_edit.html", {"title": title})
temp = request.POST.get("department")
# print(temp)
# 多个字段更新 在每个字段后面加上逗号
models.DepartMent.objects.filter(id=nid).update(title=temp, )
return redirect("/depart/list")
def user_list(request):
data = models.UserInfo.objects.all()
# 这里是通过Django的Python代码来实现的
# for i in data:
# # 将datetime 类型转化为字符串类型
# # i.create_time = dt.strftime("%Y-%m-%d %H:%M:%S")
# i.create_time = dt.strftime("%Y-%m-%d")
# # 可以直接通过get_字段名_display()获取choices的值
# i.gender = i.get_gender_display()
# # 通过部门id获得部门名称 会将id自动转化名称
# i.depart_id = i.depart.title
return render(request, "user_list.html", {"user_list": data})
def user_add(request):
if request.method == "GET":
data = {
"gender_choices": models.UserInfo.gender_choice,
"depart_list": models.DepartMent.objects.all()
}
return render(request, "user_add.html", data)
username = request.POST.get("name")
password = request.POST.get("password")
age = request.POST.get("age")
account = request.POST.get("account")
create_time = request.POST.get("create_time")
depart = request.POST.get("depart")
gender = request.POST.get("gender")
print(username, password, age, gender, depart, account, create_time)
models.UserInfo.objects.create(name=username, password=password, gender=gender, depart_id=depart, age=age,
account=account, create_time=create_time)
return redirect("/user/list")
# class UserModelForm(BootStrapModelForm):
# # 自定义字段
# name = forms.CharField(min_length=2, label="姓名",
# error_messages={"required": "不能为空", "min_length": "长度不能小于2"})
# # 如果要使用正则表达式验证,可以使用validators
# password = forms.CharField(min_length=6, label="密码",
# error_messages={"required": "不能为空", "min_length": "长度不能小于6"})
# age = forms.IntegerField(max_value=100, label="年龄",
# error_messages={"required": "不能为空", "max_value": "不能大于100"})
#
# class Meta:
# model = models.UserInfo
# fields = "__all__"
# # depart 不要写成 depart_id 它可以自动找到命名
# # fields = ["name", "password", "age", "account", "depart", "gender","create_time"]
# # widgets = {
# # "name": forms.TextInput(attrs={"class": "form-control"}),
# # "password": forms.TextInput(attrs={"class": "form-control"}),
# # "age": forms.TextInput(attrs={"class": "form-control"}),
# # "account": forms.TextInput(attrs={"class": "form-control"}),
# # "depart": forms.Select(attrs={"class": "form-control"}),
# # "gender": forms.Select(attrs={"class": "form-control"}),
# # "create_time": forms.TextInput(attrs={"class": "form-control"}),
# # }
#
# # def __init__(self, *args, **kwargs):
# # super().__init__(*args, **kwargs)
# # for name, item in self.fields.items():
# # # item.widget.attrs.update({"class": "form-control", "placeholder": "请输入%s" % (item.label,)})
# # item.widget.attrs = {"class": "form-control", "placeholder": "请输入%s" % (item.label,)}
def user_modelform_add(request):
if request.method == "GET":
form = UserModelForm()
return render(request, "modelform_add.html", {"form": form})
form = UserModelForm(data=request.POST)
if form.is_valid():
print(form.cleaned_data)
form.save()
return redirect("/user/list")
else:
print(form.errors)
return render(request, "modelform_add.html", {"form": form})
def user_edit(request, nid):
instance_data = models.UserInfo.objects.filter(id=nid).first()
if request.method == "GET":
form = UserModelForm(instance=instance_data)
return render(request, "user_edit.html", {"form": form})
# 更新数据
form = UserModelForm(data=request.POST, instance=instance_data)
if form.is_valid():
print(form.cleaned_data)
# 如果想保存用户输入以外的其他值,可以使用以下方法
# form.instance.depart_id = request.POST.get("depart")
form.save()
return redirect("/user/list")
else:
print(form.errors)
return render(request, "user_edit.html", {"form": form})
def user_delete(request, nid):
models.UserInfo.objects.filter(id=nid).delete()
return redirect("/user/list")
def phone_list(request):
# for i in range(300):
# models.PhoneNumber.objects.create(mobile="12345678901", price=100, level=1, status=1)
data = {}
# models.PhoneNumber.objects.filter(id=1)
search_ = request.GET.get("q")
# 传过来的值可能为空
if search_:
data["mobile__contains"] = search_
# return render(request, "phone_list.html", {"data": data})
# 默认为第一页 不写有问题
page = request.GET.get("page", 1)
page = int(page)
# 每页的数据大小
page_size = 10
# 根据要访问的页码计算出起止位置
start = (page - 1) * page_size
end = page * page_size
# 可以进行排序 -level 表示按照级别降序
# data = models.PhoneNumber.objects.all().order_by("-level")
data = models.PhoneNumber.objects.filter(**data).order_by("-level")[start:end]
# 数据总数
total_count = models.PhoneNumber.objects.all().count()
# 总页数
page_total, div = divmod(total_count, page_size)
if div > 0:
page_total += 1
# 计算出显示当前页码的前五页,后五页
plus = 5
# 数据库的数据较少 页码全部显示
if page_total < 2 * plus + 1:
start_page = 1
end_page = page_total + 1
else:
# 数据库的数据较多
if page <= plus:
start_page = 1
end_page = 2 * plus + 1
elif page > page_total - plus:
start_page = page_total - 2 * plus
end_page = page_total + 1
else:
start_page = page - plus
end_page = page + plus + 1
# 生成页码
page_data = []
for i in range(start_page, end_page):
if i == page:
hl = '<li class="active"><a href="?page={}">{} <span class="sr-only">(current)</span></a></li>'.format(i, i)
else:
hl = '<li><a href="?page={}">{} <span class="sr-only">(current)</span></a></li>'.format(i, i)
page_data.append(hl)
# 首页 末页
shou_page = '<li><a href="?page={}">首页</a></li>'.format(1)
wei_page = '<li><a href="?page={}">末页</a></li>'.format(page_total)
# 上一页 下一页
prev_page = page - 1
next_page = page + 1
if prev_page > 1:
hl = '<li><a href="?page={}" aria-label="Previous"><span aria-hidden="true">«</span></a></li>'.format(
prev_page)
else:
hl = '<li class="disabled" disabled="True"><a href="?page={}" aria-label="Previous"><span aria-hidden="true">«</span></a></li>'.format(
1)
page_data.insert(0, hl)
if next_page <= page_total:
hl = '<li><a href="?page={}" aria-label="Next"><span aria-hidden="true">»</span></a></li>'.format(
next_page)
else:
hl = '<li class="disabled" disabled="True"><a href="?page={}" aria-label="Next"><span aria-hidden="true">»</span></a></li>'.format(
page_total)
page_data.append(hl)
page_data.insert(0, shou_page)
page_data.append(wei_page)
# 跳转页面
jump_page = request.GET.get("jump")
# jump_page = int(jump_page)
if jump_page is not None:
jump_page = int(jump_page)
if jump_page <= 1:
jump_page = 1
elif jump_page > page_total:
jump_page = page_total
return redirect("/phone/list?page=%s" % (jump_page,))
page_data = mark_safe("".join(page_data))
return render(request, "phone_list.html",
{"data": data, "search": search_, "page_data": page_data, "current_page": page})
# class PhoneModelForm(BootStrapModelForm):
# # 方式一:在字段中添加验证规则
# # validator中可以添加多个验证规则
# # mobile = forms.CharField(
# # min_length=11,
# # label="手机号",
# # validators=[RegexValidator(r"^1\d{10}$", "手机号格式错误")]
# # )
#
# class Meta:
# model = models.PhoneNumber
# fields = "__all__"
# # fields = ["mobile", "price", "level", "status"]
# # 排除哪个字段
# # exclude = ["status"]
#
# # def __init__(self, *args, **kwargs):
# # super().__init__(*args, **kwargs)
# # for name, item in self.fields.items():
# # if item.widget.attrs:
# # item.widget.attrs["class"] = "form-control"
# # item.widget.attrs["placeholder"] = "请输入%s" % (item.label,)
# # else:
# # item.widget.attrs = {"class": "form-control", "placeholder": "请输入%s" % (item.label,)}
#
# # 方式二:钩子方法
# def clean_mobile(self):
# mobile_ = self.cleaned_data["mobile"]
# exits = models.PhoneNumber.objects.filter(mobile=mobile_).exists()
# if exits:
# raise forms.ValidationError("手机号已存在")
# # 验证不通过
# if len(mobile_) != 11:
# raise forms.ValidationError("手机号长度不对")
# for i in mobile_:
# if i not in "0123456789":
# raise forms.ValidationError("手机号格式错误,必须全是数字")
#
# return mobile_
def phone_add(request):
if request.method == "GET":
form = PhoneModelForm()
return render(request, "phone_add.html", {"form": form})
form = PhoneModelForm(data=request.POST)
if form.is_valid():
form.save()
return redirect("/phone/list")
else:
print(form.errors)
return render(request, "phone_add.html", {"form": form})
def phone_edit(request, nid):
instance_data = models.PhoneNumber.objects.filter(id=nid).first()
if request.method == "GET":
form = PhoneEditModelForm(instance=instance_data)
return render(request, "phone_edit.html", {"form": form})
form = PhoneEditModelForm(data=request.POST, instance=instance_data)
if form.is_valid():
form.save()
return redirect("/phone/list")
else:
print(form.errors)
return render(request, "phone_edit.html", {"form": form})
# class PhoneEditModelForm(BootStrapModelForm):
# mobile = forms.CharField(disabled=True, label="手机号")
#
# class Meta:
# model = models.PhoneNumber
# # fields = "__all__"
# # 不让用户修改手机号
# fields = ["mobile", "price", "level", "status"]
# # 排除哪个字段
# # exclude = ["status"]
#
# #
# # def __init__(self, *args, **kwargs):
# # super().__init__(*args, **kwargs)
# # for name, item in self.fields.items():
# # item.widget.attrs = {"class": "form-control", "placeholder": "请输入%s" % (item.label,)}
#
# def clean_mobile(self):
# mobile_ = self.cleaned_data["mobile"]
# exits = models.PhoneNumber.objects.exclude(id=self.instance).filter(mobile=mobile_).exists()
# if exits:
# raise forms.ValidationError("手机号已存在")
# return mobile_
def phone_delete(request, nid):
models.PhoneNumber.objects.filter(id=nid).delete()
return redirect("/phone/list")
管理员功能
表结构的设计
ID | 用户名 | 密码 |
---|---|---|
1 | class Admin(models.Model): |
管理员列表
有了模板与工具类之后,编写列表变得很方便
需要注意的是,获取所有信息放在了Pagination
工具类中
1 | from django.shortcuts import render, redirect, HttpResponse |
关于搜索
分页功能是有bug的,比如我搜索只后,点击下一页会直接从admin/list?q=123
跳到admin/list?page=2
,这是因为跳转链接并没有和搜索q
参数产生关联。需要了解以下方法:
1 | request.GET # 这个对象会返回连接中的所有参数 |
如:
发送GET请求:admin/list?aaa=123&tys=456&jjj=789
得到的结果为:
1 | <QueryDict: {'aaa': ['123'], 'tys': ['456'], 'jjj': ['789']}> |
另外可以使用setlist(参数名,参数值列表)
来设定一些特定的参数值,但是一般来说,这个方法是不允许使用的,原因为request.GET._mutable = False
,也就是说默认并不让修改。
这时候可以使用copy
的方法复制对象,然后再修改其中的_mutable = True
1 | import copy |
返回的链接为:aaa=123&tys=456&jjj=789&page=1
因此我们可以重新编写分页的链接逻辑
1 | import copy |
下面是生成链接的一个例子:
1 | for i in range(start_page, end_page): |
添加账户
由于每次我们使用的添加和编辑页面几乎都相同,因此可以创建一个模板html
1 | {% extends "layout.html" %} |
开放title来让设定标题
1 | def admin_add(request): |
这边要注意的是密码的加密,因为密码是不能明文储存的,我们这边使用常见的MD5加密
1 | from django.conf import settings |
定义一个ModelFrom
1 | class AdminModelForm(BootStrapModelForm): |
self.cleaned_data
可以获得当前表单的所有值,因为常规的交互逻辑是需要有一个确认密码的机制,可以添加一个新的字段,其中render_value=True
可以让刷新页面时候密码保留而不消失
1 | confirm_password = forms.CharField( |
编辑账户
推荐只能编辑账户的用户名,由于需求不一致,需要重新设定ModelForm
1 | class AdminEditModelForm(BootStrapModelForm): |
直接使用change.html
作为模板
1 | def admin_edit(request, nid): |
重置密码
这是常见的功能之一,关键点在于两点:
- 不能让用户看到之前存储的哈希值
- 重置的密码不能与上一次的一致
要获取当前的输入值,我们可以使用self.instance
来获取,使用self.instance.pk
获取当前的id值
1 | class AdminResetModelForm(BootStrapModelForm): |
由于不能让用户看到之前的密码储存值,因此不给AdminResetModelForm
传递参数
1 | def admin_reset(request, nid): |
登录功能
首先要明白什么是cookie和session
http协议的特点为:无状态 & 短连接
为了让用户保持登录状态,服务器会为用户生成随机但唯一的键值对cookie,作为用户的访问凭证,但是这样可能会出现乱输入cookie伪造登录的情况,为了解决这个问题,需要在服务端将已发放的cookie存储下来,这样可以对用户发来的cookie值进行比对。
在Django中使用request.session
方法可以生成随机字符串,并将其存入django.session
数据库中
1 | from django.shortcuts import render, HttpResponse,redirect |
request.session["info"] = {"id": create.id, "username": create.username}
这段代码的意思是生成随机cookie,并将{“id”: create.id, “username”: create.username}作为数据存入
尝试登录
进入数据库查看
cookie是以浏览器为单位进行生成,而不是账户
使用chrome按照相同的账户进行登录
此时还将BootstrapModel.py
修改如下:
1 | from django import forms |
登录验证
登录成功后:
- cookie,随机字符串
- session,用户信息
在其他需要登录才能访问的页面中,都需要加入:
1 | def admin_list(request): |
太麻烦,需要在18个视图函数中统一添加上述的判断
中间件
如何定义一个中间件
1 | from django.utils.deprecation import MiddlewareMixin |
process_response
必须返回response
在中间件的process_request
有返回值则不再继续后续的操作,直接返回
应用中间件需要在settings.py
中添加,添加顺序即为访问顺序
1 | MIDDLEWARE = [ |
登录的中间件
1 | from django.utils.deprecation import MiddlewareMixin |
一定要排除一些登录界面,不然会一直出现重定向
登出只需要清除网页的cookie信息,数据库中的信息并没有删除
1 | def logout(request): |
登录成功的信息需要在layoout.html
中使用来获取当前登录的用户名,之后就能在右上角看到用户信息
优化登录(图片验证码)
1 | pip install pillow |
生成验证码
1 | from PIL import Image, ImageDraw, ImageFont |
对于图片,保存到本地之后再读取有点麻烦,直接将其写入内存即可
1 | img, code_str = create_img_code() |
我们将图片放在/image/code
连接中,需要现在中间件中排除,不然无法访问这个链接,也无法获得图片
1 | def process_request(self, request): |
完整的视图函数
1 | from io import BytesIO |
按照规定修改Form
1 | class LoginForm(BootStrapForm): |
1 |
|
为了验证验证码我们需要做两件事
- 存储当前的验证码 存储在session中
- 校验用户提交的验证码 并设定相关的状态
1 | def image_code(request): |
1 | if form.is_valid(): |
Ajax请求
浏览器向我们的网站发送请求时:URL和表单的方式
- GET
- POST
这样的方法有个特点,每次都会导致页面刷新。
除此之外,也可以基于Ajax向后台发送请求(偷偷发送请求)
依赖JQuery
编写Ajax代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14$.ajax(
{
url:"发送的地址",
type:"post",
data:{
n1:123,
n2:456
},
success:function(res){
console.log(res);
}
}
)
前端骨架
1 | {% extends "layout.html" %} |
发送GET请求
1 | $.ajax({ |
1 | def task_ajax(request): |
发送POST请求
1 | $.ajax({ |
1 | from django.views.decorators.csrf import csrf_exempt |
基于jquery
1 | {% extends "layout.html" %} |
基于Ajax的返回值
一般都会返回一个json格式
1 | from django.http import JsonResponse |
有两种方式
1 | data_dict = { |
1 | data_dict = { |
前端想要使用的话需要修改如下
1 | $(function () { |
任务列表
创建models
1 | class TaskList(models.Model): |
创建form
1 | class TaskModelForm(BootStrapModelForm): |
创建视图函数,接受数据并返回信息
1 | def task_add(request): |
在前端中进行展示,这边有个注意点是button点击后会默认刷新页面,这时候有两种方法
- 改为input标签
- 在js中添加
event.preventDefault();
另外,为了防止标签填充出现的乱套情况,需要将form-group
的位置设置为relative,span的位置设置为absolute
1 | <div class="panel panel-default"> |
js方面,为了让错误信息清楚,在每次点击之前都清除所有span的信息
1 | $(function () { |
使用ajax不会自动刷新页面,所以我们需要使用js进行页面的刷新
1 | if (res.status) { |
订单列表
添加订单
设计表结构
id | 订单号 | 商品名称 | 价格 | 状态 | 用户ID |
---|---|---|---|---|---|
自动生成 | 当前时间+随机数字 | 已支付/未支付 | 关联用户 |
1 | class Orders(models.Model): |
准备使用模态框来实现添加订单的功能
1 | <div id="exampleModal" class="modal fade" tabindex="-1" role="dialog"> |
让模态框绑定按钮有两种方法
- 使用js
- 使用
data-target:类名 data-toggle="modal"
1 | <!-- 第一种:使用js --> |
1 | $(function () { |
要填写数据,需要先定义ModelForm,其中有两个值不允许用户自己填写
- oid - 由系统自动生成
- admin - 自动获取
1 | class OrderModelForm(BootStrapModelForm): |
将表单写入模态框
1 | <div class="panel panel-default"> |
1 | def order_list(request): |
编写添加的逻辑
1 | from django.views.decorators.csrf import csrf_exempt |
使用form.instance.属性名
来直接设置其中的值
为提交按钮绑定js
1 | function bindSaveEvent() { |
删除订单
使用ajax来实现删除
但是这样有个问题,如何获取当前删除数据的id
- 偷偷将id藏到一个地方
- 定义成全局变量
采用自定义属性的方式获取到当前的id
1 | <tbody> |
在js中可以通过$(this).attr("uid")
获取到当前的uid
1 | let DELETE_ID = 0; |
删除的逻辑
1 | function bindDeleteConfirmEvent() { |
1 | def order_delete(request): |
编辑订单
编辑订单也必须从后台获得当前id的所有数据,而且由于编辑与添加功能相似,因此使用同一个前端对话框,但是需要修改相应的逻辑
1 | function bindSaveEvent() { |
设定一个常亮来标记当前对话框的使用状态EDIT_ID
:
undefined - 添加
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28function addDate() {
$.ajax({
url: "/order/add",
type: "post",
data: $('#formAdd').serialize(),
dataType: 'json',
success: function (res) {
console.log(res)
if (res.status) {
alert("添加成功")
// 清空表单 取0得到DOM对象
$("#formAdd")[0].reset();
// 关闭对话框
$("#exampleModal").modal("hide");
// 刷新页面
location.reload()
} else {
console.log(res.error)
$.each(res.error, function (name, value) {
console.log(name, value)
// console.log($("[name=" + name + "]"))
// 找到对应的id,然后找到下一个元素,设置文本
$("#id_" + name).next().text(value[0])
})
}
}
})
}uid - 编辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33function editDate() {
$.ajax({
url: "/order/edit" + "?uid=" + EDIT_ID,
type: "post",
data: $('#formAdd').serialize(),
dataType: 'json',
success: function (res) {
console.log(res)
if (res.status) {
alert("编辑成功")
// 清空表单 取0得到DOM对象
$("#formAdd")[0].reset();
// 关闭对话框
$("#exampleModal").modal("hide");
// 刷新页面
location.reload()
} else {
console.log(res.error)
if (res.error) {
$.each(res.error, function (name, value) {
console.log(name, value)
// console.log($("[name=" + name + "]"))
// 找到对应的id,然后找到下一个元素,设置文本
$("#id_" + name).next().text(value[0])
}
)
} else {
alert(res.tips)
}
}
}
})
}
为了将所有的数据展示在对话框上,我们需要将数据传到前端
1 | def order_detail(request): |
想要去数据库中获取数据时,这里需要区分三个不同的返回值
1 | row_object = models.Orders.objects.filter(id=uid).first() |
1 | # 返回值是一个字典 {"id":1,"uid":454545,……} |
1 | # [{"id":1,"uid":454545,……},{"id":2,"uid":454545,……}] |
1 | # 返回值是一个元组[(1,"xx"),(2,"xxxx")] |
最好直接获取字典
1 |
|
使用不同的返回值tips
与error
来分辨是哪种错误
图表
- highchart 国外
- echarts 国内百度开源
- 引入js
- 使用ajax传送数据
文件的上传
获取文件对象
1 | <div class="container"> |
对于文件的上传,一定要加上enctype="multipart/form-data"
这个属性,如果不加,那么只能获得文件的名称,而不能获得相应的文件
1 | from django.shortcuts import render, HttpResponse |
输出的结果分别为:
1 | <QueryDict: {'csrfmiddlewaretoken': ['EJkSoxCazcl7eh3UVQSZMIKU5jcjJrXtcD9mTKJIuqPkpsRZNtr0zNMlo5AcKBcP'], 'username': ['BA']}> |
POST请求中并没有文件,文件在request.FILES
中
获得的是一个文件对象,文件内部有许多的属性
1 | def file_upload(request): |
打印结果
1 | # post请求的数据 |
文件的校验:批量上传数据
要进行数据的校验,需要使用Modelform和form,案例是使用excel批量上传数据
关于excel的处理问题,需要使用第三方库
1 | pip install openpyxl |
使用方法
1 | from openpyxl import load_workbook |
对应的前端页面为:
1 | <div id="exampleModal" class="modal fade" tabindex="-1" role="dialog"> |
案例:混合数据(form)
提交页面时,用户输入数据+文件(输入不能为空,报错)
表单设计如下:
1 | class uploadForm(BootStrapForm): |
这边为了取消文件上传的样式修改了
1 | from django import forms |
数据库设计如下:
1 | class Boss(models.Model): |
这边要注意的就是为什么图像时charfield类型,因为我们一般不直接存图像数据,一般是改为存图像的路径。
对于文件路径,推荐采用os来拼接,这样能适应多样的系统环境
1 | def upload_form(request): |
对于路径存放问题,目前来说,Django只能读取static中的文件对象,当然它也可以读取其他的路径的文件,这在后续会说
1 | file_path = os.path.join("management_sys", db_file_path) |
启用media目录
在Django的开发过程中两个特殊文件夹:
- static,存放静态文件的路径,包括:css,js,项目图片。
- media,用户上传的数据的目录(需要启用)。
在url.py中进行配置
1 | from django.urls import path, re_path |
在settings.py中进行配置
1 | import os |
之后就可以在浏览器中访问这个文件
这样就将上传与静态文件分开了
修改之前的保存文件路径
1 | from django.conf import settings |
Modelform案例
前提是配置好media目录
1 | class City(models.Model): |
在视图函数中
1 | def upload_modelform(request): |
上传文件小结
自己动手去写
1
2form = request.FILES.get("ff")
……form组件
1
2
3
4
5
6
7
8
9
10
11
12
13form = uploadForm(data=request.POST, files=request.FILES)
# 将文件路径存到数据库
data = {
"name": form.cleaned_data.get("name"),
"age": form.cleaned_data.get("age"),
"img": db_file_path
}
# 这样的文件路径存储方式不好,用户是看不的
# 因为Django是只能展示static文件夹下的文件
models.Boss.objects.create(**data)
具体的文件操作还是得自己写(比如文件的保存)modelform组件(表单验证+自动保存数据库+自动保存文件)
1
2
3- media文件夹
- model.py的类型定要
img = models.FileField(max_length=128, verbose_name="Logo", upload_to="city/")