文章目录
上一篇文章: Django 03 :员工管理【 模板继承 + Form + ModelForm】
1、用户管理:编辑与删除
1.1、编辑用户
任务:
点击编辑,跳转到编辑页面(将编辑行的ID携带过去)。
编辑页面(显示所编辑用户的默认数据 ==> 根据ID获取并设置到页面中)
提交:
错误提示
数据校验
在数据库更新
models.UserInfo.filter(id=4).update(...)
(1)前端基础效果
- urls.py
path('user/<int:nid>/edit/', views.user_edit),
- user_list.py
<td>
<a class="btn btn-primary btn-xs" href="/user/{{ obj.id }}/edit/">编辑</a>
<a class="btn btn-danger btn-xs" href="#">删除</a>
</td>
- user_edit.html
可以copyuser_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> {# novalidate : 关闭浏览器帮我们做的校验,用自己的 #}
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label>{{ field.label }}</label>
{{ field }}
<span style="color: red;">{{ field.errors.0 }}</span> {# 错误信息可能有很多,我们只显示第0个就好了 #}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">提 交</button>
</form>
</div>
</div>
</div>
{% endblock %}
如何展示默认数据呢?
views.py
def user_edit(request, nid):
"""编辑用户"""
# 根据ID获取数据库中数据
row_object = models.UserInfo.objects.filter(id=nid).first()
form = MyForm(instance=row_object) # 使用 instance ,ModelForm就可以默认将数据显示出来
return render(request, 'user_edit.html', {"form": form})
就这几行代码即可实现基本效果:
(2)提交
修改一下视图函数(views.py)就OK了
- views.py
def user_edit(request, nid):
"""编辑用户"""
if request.method == "GET":
# 根据ID获取数据库中数据
row_object = models.UserInfo.objects.filter(id=nid).first()
form = MyForm(instance=row_object) # 使用 instance ,ModelForm就可以默认将数据显示出来
return render(request, 'user_edit.html', {"form": form})
row_object = models.UserInfo.objects.filter(id=nid).first() # 这行代码勿忘,否则就成新增用户为不是编辑用户了
form = MyForm(data=request.POST, instance=row_object)
if form.is_valid():
form.save()
return redirect('/user/list/')
# 如果数据不合格,则返回"user_edit.html"
return render(request, 'user_edit.html', {"form": form})
效果展示:
(3)补充知识
1、小优化:员工的入职时间精确到日就OK了
- models.py
# create_time = models.DateTimeField(verbose_name="入职时间")
create_time = models.DateField(verbose_name="入职时间")
注意,既然变更了数据库,就要执行那俩指令了
python manage.py makemigrations
python manage.py migrate
2、form.save() 默认保存的是用户输入的所有数据,如果想要再用户输入以外增加一点值
- views.py
if form.is_valid():
# 默认保存的是用户输入的所有数据,如果想要再用户输入以外增加一点值
# form.instance.字段名 = 值 如:form.age = 20
form.save()
return redirect('/user/list/')
1.2、删除用户
基本和之前写的【删除部门】一样
-
user_list.html
添加 href
<a class="btn btn-danger btn-xs" href="/user/{{ obj.id }}/detele">删除</a>
-
urls.py
path("user/<int:nid>/detele/", views.user_detele),
-
views.py
def user_detele(request, nid): """删除用户""" models.UserInfo.objects.filter(id=nid).delete() return redirect("/user/list/")
这样就完成删除功能了
2、靓号管理
靓号:通迅所使用的手机号码
2.1、数据库的表结构
首先就是设计表结构
根据表结构的需求,在models.py
中创建类(由类生成数据库中的表)。
models.py
class MobileNum(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) # default=1 : 默认为 1
status_choices = (
(1, "已占用"),
(2, "未使用")
)
status = models.SmallIntegerField(verbose_name="状态", choices=status_choices, default=2)
python manage.py makemigrations
python manage.py migrate
我们先用SQL语句创建点数据:
insert into app01_mobilenum(mobile,price,level,status)values("111111111",19,1,1);
2.2、靓号列表
URL
函数
获取所有的靓号
结合
html
+render
将靓号罗列出来id 号码 价格 级别(中文) 状态(中文)
开始敲键盘
-
urls.py
path("mobile/list/", views.mobile_list),
-
views.py
def mobile_list(request): """ 靓号列表 """ queryset = models.MobileNum.objects.all().order_by("-level") return render(request, 'mobile_list.html', {"queryset": queryset})
-
mobile_list.html
直接copy
user_list.html
, 修改文字即可{% extends 'layout.html' %} {% block content %} {# 用户列表 #} <div class="container"> <div style="margin-bottom: 10px"> <a class="btn btn-success" href="/user/add/"> {# 可以添加一个 target="_blank" : 使页面在新页面打开,如果不设置,会在原页面打开(这里我们还是在当前页面打开) #} <span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span> 新建用户 </a> {#新建一个 depart_add.html , 用来写新建部门界面#} </div> <div class="panel panel-default"> <!-- Default panel contents --> <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> <th>{{ obj.id }}</th> <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="/user/{{ obj.id }}/edit/">编辑</a> <a class="btn btn-danger btn-xs" href="/user/{{ obj.id }}/detele">删除</a> </td> </tr> {% endfor %} </tbody> </table> </div> </div> {% endblock %}
-
layout.html
在导航条添加【靓号管理】
<ul class="nav navbar-nav"> <li><a href="/depart/list/">部门管理</a></li> <li><a href="/user/list/">用户管理</a></li> <li><a href="/mobile/list/">靓号管理</a></li> </ul>
效果展示:
2.3、靓号:新建、编辑、删除
列表点击跳转:
/pretty/add/
URL
ModelForm类
from django import forms class MobileModelForm(forms.ModelForm): ...
函数
- 实例化类的对象
- 通过render将对象传入到HTML中。
- 模板的循环展示所有的字段。
点击提交
- 数据校验
- 保存到数据库
- 跳转回靓号列表
从【用户管理】那Copy就OK了,删删改改就完成了基础的【靓号管理】
(1)新建靓号
-
urls.py
path("mobile/list/", views.mobile_list), path("mobile/add/", views.mobile_add),
-
mobile_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> {# novalidate : 关闭浏览器帮我们做的校验,用自己的 #} {% csrf_token %} {% for field in form %} <div class="form-group"> <label>{{ field.label }}</label> {{ field }} <span style="color: red;">{{ field.errors.0 }}</span> {# 错误信息可能有很多,我们只显示第0个就好了 #} </div> {% endfor %} <button type="submit" class="btn btn-primary">提 交</button> </form> </div> </div> </div> {% endblock %}
-
views.py
class MobileModelForm(forms.ModelForm): """靓号管理""" class Meta: model = models.MobileNum fields = ["mobile", "price", "level", "status"] 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} def mobile_add(request): """"添加靓号""" if request.method == "GET": form = MobileModelForm() return render(request, "mobile_add.html", {"form": form}) form = MobileModelForm(data=request.POST) if form.is_valid(): print(form.cleaned_data) form.save() return redirect("/mobile/list/") return render(request, 'mobile_add.html', {"form": form})
效果展示:
优化(添加验证)
通过正则表达式,限制输入格式
views.py
from django.core.validators import RegexValidator
class MobileModelForm(forms.ModelForm):
"""靓号管理"""
mobile = forms.CharField(
label="手机号",
validators=[RegexValidator(r'^1[3-9]\d{9}', "手机号格式错误"),], # 通过正则表达式限制输入格式,1开头,然后3~9,然后再来就个数
# 当然这个正则表达式可以叠加,多重限制
)
class Meta:
效果展示:
可能遇到的bug:
为何惊现双输入框?!
原因:
虚晃一枪
然后:
TypeError: 'str' object is not callable
从报错说某个变量不可调用,找了半天才发现,括号打错地方了
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
class MobileModelForm(forms.ModelForm):
"""靓号管理"""
# # 验证:方式1
# mobile = forms.CharField(
# label="手机号",
# validators=[RegexValidator(r'^1[3-9]\d{9}$', "手机号格式错误")], # 通过正则表达式限制输入格式,1开头,然后3~9,然后再来就个数
# # 当然这个正则表达式可以叠加,多重限制
# )
class Meta:
model = models.MobileNum
fields = ["mobile", "price", "level", "status"]
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("格式错误")
exists = models.MobileNum.objects.filter(mobile=txt_mobile).exists()
if exists:
raise ValidationError("手机号已存在")
# 验证通过,用户输入的值返回
return txt_mobile
效果展示:
(2)编辑靓号
仿着编辑用户来就好了
-
mobile_list.html
<a class="btn btn-primary btn-xs" href="/mobile/{{ obj.id }}/edit/">编辑</a> <a class="btn btn-danger btn-xs" href="/mobile/{{ obj.id }}/detele">删除</a>
-
urls.py
path('mobile/<int:nid>/edit/', views.mobile_edit),
-
views.py
def mobile_edit(request, nid): """编辑用户""" row_object = models.MobileNum.objects.filter(id=nid).first() # 这行代码勿忘,否则就成新增用户为不是编辑用户了 if request.method == "GET": # 根据ID获取数据库中数据 form = MobileModelForm(instance=row_object) # 使用 instance ,ModelForm就可以默认将数据显示出来 return render(request, 'mobile_edit.html', {"form": form}) form = MobileModelForm(data=request.POST, instance=row_object) if form.is_valid(): form.save() return redirect('/mobile/list/') # 如果数据不合格,则返回"user_edit.html" return render(request, 'mobile_edit.html', {"form": form})
-
mobile_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> {# novalidate : 关闭浏览器帮我们做的校验,用自己的 #} {% csrf_token %} {% for field in form %} <div class="form-group"> <label>{{ field.label }}</label> {{ field }} <span style="color: red;">{{ field.errors.0 }}</span> {# 错误信息可能有很多,我们只显示第0个就好了 #} </div> {% endfor %} <button type="submit" class="btn btn-primary">提 交</button> </form> </div> </div> </div> {% endblock %}
效果展示:
若点击【编辑】,传到的是【用户编辑】,且代码无误,重启项目即可
(3)删除靓号
-
mobile_list.html
<td> <a class="btn btn-primary btn-xs" href="/mobile/{{ obj.id }}/edit/">编辑</a> <a class="btn btn-danger btn-xs" href="/mobile/{{ obj.id }}/detele">删除</a> </td>
-
urls.p
path("mobile/<int:nid>/detele/", views.mobile_detele),
-
views.py
def mobile_detele(request, nid): """删除用户""" models.MobileNum.objects.filter(id=nid).delete() return redirect("/mobile/list/")
效果展示:
(4)优化:编辑靓号
如果我们不想【编辑靓号】的界面和【新建靓号】的界面一样
那么就要为【编辑】单写一个ModelForm
修改一下views.py
即可
-
view.py
class MobileEditModelForm(forms.ModelForm): mobile = forms.CharField(disabled=True, label="手机号") # 设置手机号不可改变 class Meta: model = models.MobileNum fields = ['mobile', 'price', 'level', 'status'] 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} def mobile_edit(request, nid): """编辑用户""" row_object = models.MobileNum.objects.filter(id=nid).first() # 这行代码勿忘,否则就成新增用户为不是编辑用户了 if request.method == "GET": # 根据ID获取数据库中数据 form = MobileEditModelForm(instance=row_object) # 使用 instance ,ModelForm就可以默认将数据显示出来 return render(request, 'mobile_edit.html', {"form": form}) form = MobileEditModelForm(data=request.POST, instance=row_object) if form.is_valid(): form.save() return redirect('/mobile/list/') # 如果数据不合格,则返回"user_edit.html" return render(request, 'mobile_edit.html', {"form": form})
效果展示:
(5)优化:手机号不可重复
添加:【正则表达式】
新建靓号时,输入的手机号不可为已存在的手机号
编辑:【正则表达式】
编辑靓号时,手机号更改后的手机号不可已存在的手机号
-
【新建靓号】
常用的仨语句
# 返回[obj,obj,obj] queryset = models.PrettyNum.objects.filter(mobile="1888888888") obj = models.PrettyNum.objects.filter(mobile="1888888888").first() # 返回 True/False exists = models.PrettyNum.objects.filter(mobile="1888888888").exists()
我们要修改【钩子】
exists = models.MobileNum.objects.filter(mobile=txt_mobile).exists() if exists: raise ValidationError("手机号已存在")
-
【编辑】
只用修改一句
exists = models.MobileNum.objects.exclude(id=self.instance.pk).filter(mobile=txt_mobile).exists()
完整展示:
class MobileEditModelForm(forms.ModelForm): # mobile = forms.CharField(disabled=True, label="手机号") # 设置手机号不可改变 class Meta: model = models.MobileNum fields = ['mobile', 'price', 'level', 'status'] 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): print(self.instance.pk) # 当前编辑的哪一行的ID txt_mobile = self.cleaned_data["mobile"] exists = models.MobileNum.objects.exclude(id=self.instance.pk).filter(mobile=txt_mobile).exists() if exists: raise ValidationError("手机号已存在") # 验证通过,用户输入的值返回 return txt_mobile
效果展示:
总结
类似这种校验方式,实际上我们只用在ModelForm中做修改,用上面讲的两种方式:
- 字段 + 正则表达式
- 钩子函数
一般验证可以放在正则表达式,深层次一点的就放在钩子函数中即可
2.4、搜索靓号
常用搜索方式:
# filter中放搜索条件
models.PrettyNum.objects.filter(mobile="19999999991",id=12)
# 或者换一种写法
data_dict = {"mobile":"19999999991","id":123}
models.PrettyNum.objects.filter(**data_dict)
对于一些变量的控制:
models.PrettyNum.objects.filter(id=12) # ID等于12
models.PrettyNum.objects.filter(id__gt=12) # ID大于12
models.PrettyNum.objects.filter(id__gte=12) # ID大于等于12
models.PrettyNum.objects.filter(id__lt=12) # ID小于12
models.PrettyNum.objects.filter(id__lte=12) # ID小于等于12
data_dict = {"id__lte":12}
models.PrettyNum.objects.filter(**data_dict)
models.PrettyNum.objects.filter(mobile="999") # 等于
models.PrettyNum.objects.filter(mobile__startswith="1999") # 筛选出以1999开头
models.PrettyNum.objects.filter(mobile__endswith="999") # 筛选出以999结尾
models.PrettyNum.objects.filter(mobile__contains="999") # 筛选出包含999
data_dict = {"mobile__contains":"999"}
models.PrettyNum.objects.filter(**data_dict)
还可以把“Go!”换成一个图标
<div style="margin-bottom: 10px" class="clearfix">
<a class="btn btn-success" href="/mobile/add/">
{# 可以添加一个 target="_blank" : 使页面在新页面打开,如果不设置,会在原页面打开(这里我们还是在当前页面打开) #}
<span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>
新建靓号
</a>
{#新建一个 depart_add.html , 用来写新建部门界面#}
<div style="float: right;width: 300px">
{# 搜索框 #}
<div class="col-lg-6">
<div class="input-group">
<input type="text" class="form-control" placeholder="Search for...">
<span class="input-group-btn">
<button class="btn btn-default" type="button">Go!</button>
</span>
</div>
</div>
</div>
</div>
效果展示:
若遇到搜索框偏左,勿忘设置宽度属性width: 300px
前端效果出来了,下面就是如何真正实现【搜索】
【后端】
views.py
def mobile_list(request):
""" 靓号列表 """
data_dict = {}
search_result = request.GET.get('q', "") # 如果有值就返回“q”,如果没有就返回空“”
if search_result:
data_dict["mobile__contains"] = search_result
queryset = models.MobileNum.objects.filter(**data_dict).order_by("-level")
return render(request, 'mobile_list.html', {"queryset": queryset, "search_result": search_result})
【前端】:修改三点
-
建立form
<form method="get"> ... </form>
-
对设置
name
和value
<input type="text" name="q" class="form-control" placeholder="Search for..." value="{{ search_result }}">
-
对搜索键添加属性
type="submit"
<button class="btn btn-default" type="button" type="submit">
mobile_list.html
代码展示:
<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_result }}">
<span class="input-group-btn">
<button class="btn btn-default" type="submit">
<span class="glyphicon glyphicon-search" aria-hidden="true"></span>
</button>
</span>
</div>
</form>
</div>
若没有搜索效果,可能原因:
objects.filter(**data_dict)
而不是 objects.all()
queryset = models.MobileNum.objects.filter(**data_dict).order_by("-level")
效果展示:
2.5、分页
具体见视频:2022 B站最详细django3教程(django从入门到实践)_哔哩哔哩_bilibili
内容暂省略
当靓号多了,我们不可能一个界面去展示,应该【分页】展示
(1)先造点数据
def mobile_list(request):
""" 靓号列表 """
for i in range(300):
models.MobileNum.objects.create(mobile="12343212343", price=20, level=1, status=1)
(2)页码前端效果
(3)分页
大致原理:
一次显示十个数据、
queryset = models.PrettyNum.objects.all()
queryset = models.PrettyNum.objects.filter(id=1)[0:10]
# 第1页
queryset = models.PrettyNum.objects.all()[0:10]
# 第2页
queryset = models.PrettyNum.objects.all()[10:20]
# 第3页
queryset = models.PrettyNum.objects.all()[20:30]
data = models.PrettyNum.objects.all().count()
data = models.PrettyNum.objects.filter(id=1).count()
然后写一些逻辑,让代码按数据量自己分页,达成下面效果:
其中比较繁琐,具体看视频,暂略
2.6、分页组件的封装
任务:希望把【分页】这个功能打包成组件,方便以后使用(可以迁移到用户管理等界面)
-
封装分页类
- 从头到尾开发
- 写项目用【pagination.py】公共组件。
-
注意:搜索 + 分页情况下,存在小Bug,搜索和分页不能同时正常进行,这个需要特殊处理一下。
分页时候,保留原来的搜索条件 http://127.0.0.1:8000/pretty/list/?q=888 http://127.0.0.1:8000/pretty/list/?page=1 http://127.0.0.1:8000/pretty/list/?q=888&page=23
直接上代码:
-
views.py
关键代码:
page_object = Pagination(request, queryset) context = { "search_data": search_result, "queryset": page_object.page_queryset, # 分完页的数据 "page_string": page_object.html() # 页码 } return render(request, 'mobile_list.html', context)
整体代码:
from app01.utils.pagination import Pagination def mobile_list(request): """ 靓号列表 """ data_dict = {} search_result = request.GET.get('q', "") # 如果有值就返回“q”,如果没有就返回空“” if search_result: data_dict["mobile__contains"] = search_result queryset = models.MobileNum.objects.filter(**data_dict).order_by("-level") page_object = Pagination(request, queryset) context = { "search_data": search_result, "queryset": page_object.page_queryset, # 分完页的数据 "page_string": page_object.html() # 页码 } return render(request, 'mobile_list.html', context)
-
app01/utils/pagination.py
""" 自定义的分页组件,以后如果想要使用这个分页组件,你需要做如下几件事: 在视图函数中: def pretty_list(request): # 1.根据自己的情况去筛选自己的数据 queryset = models.PrettyNum.objects.all() # 2.实例化分页对象 page_object = Pagination(request, queryset) context = { "queryset": page_object.page_queryset, # 分完页的数据 "page_string": page_object.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中传递的获取分页的参数,例如:/etty/list/?page=12 :param plus: 显示当前页的 前或后几页(页码) """ from django.http.request import QueryDict import copy query_dict = copy.deepcopy(request.GET) query_dict._mutable = True self.query_dict = query_dict self.page_param = page_param page = request.GET.get(page_param, "1") if page.isdecimal(): page = int(page) else: page = 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: # 数据库中的数据比较少,都没有达到11页。 start_page = 1 end_page = self.total_page_count else: # 数据库中的数据比较多 > 11页。 # 当前页<5时(小极值) if self.page <= self.plus: start_page = 1 end_page = 2 * self.plus + 1 else: # 当前页 > 5 # 当前页+5 > 总页面 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 + 1): 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())) search_string = """ <li> <form style="float: left;margin-left: -1px" method="get"> <input name="page" style="position: relative;float:left;display: inline-block;width: 80px;border-radius: 0;" type="text" class="form-control" placeholder="页码"> <button style="border-radius: 0" class="btn btn-default" type="submit">跳转</button> </form> </li> """ page_str_list.append(search_string) page_string = mark_safe("".join(page_str_list)) return page_string
-
mobile_list.html
关键代码:在mobile.html底部添加【分页】
{# 分页 #} <div class="clearfix"> <ul class="pagination"> {{ page_string }} </ul> </div>
效果展示:
使用分页组件
如何使用我们这个【分页组件】呢?三步走
-
准备好
pagination.py
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中传递的获取分页的参数,例如:/etty/list/?page=12 :param plus: 显示当前页的 前或后几页(页码) """ from django.http.request import QueryDict import copy query_dict = copy.deepcopy(request.GET) query_dict._mutable = True self.query_dict = query_dict self.page_param = page_param page = request.GET.get(page_param, "1") if page.isdecimal(): page = int(page) else: page = 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: # 数据库中的数据比较少,都没有达到11页。 start_page = 1 end_page = self.total_page_count else: # 数据库中的数据比较多 > 11页。 # 当前页<5时(小极值) if self.page <= self.plus: start_page = 1 end_page = 2 * self.plus + 1 else: # 当前页 > 5 # 当前页+5 > 总页面 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 + 1): 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())) search_string = """ <li> <form style="float: left;margin-left: -1px" method="get"> <input name="page" style="position: relative;float:left;display: inline-block;width: 80px;border-radius: 0;" type="text" class="form-control" placeholder="页码"> <button style="border-radius: 0" class="btn btn-default" type="submit">跳转</button> </form> </li> """ page_str_list.append(search_string) page_string = mark_safe("".join(page_str_list)) return page_string
-
修改视图函数
from app01.utils.pagination import Pagination def mobile_list(request): """ 靓号列表 """ data_dict = {} search_result = request.GET.get('q', "") # 如果有值就返回“q”,如果没有就返回空“” if search_result: data_dict["mobile__contains"] = search_result queryset = models.MobileNum.objects.filter(**data_dict).order_by("-level") page_object = Pagination(request, queryset) context = { "search_data": search_result, "queryset": page_object.page_queryset, # 分完页的数据 "page_string": page_object.html() # 页码 } return render(request, 'mobile_list.html', context)
-
修改前端,如mobile_list.html
{# 分页 #} <div class="clearfix"> <ul class="pagination"> {{ page_string }} </ul> </div>
下面我们在【用户管理界面】使用【分页组件】
3、时间插件
之前填写入职时间,我们是纯手工输入,这样就容易出现格式问题,如何规范化输入呢?
要实现的效果:
我们直接看一下总共要加入那些东西:
- 插件(css、js)
- 修改(引入 id )
- 添加【js函数】
<link rel="stylesheet" href="static/plugins/bootstrap-3.4.1/css/bootstrap.css">
<link rel="stylesheet" href="static/plugins/bootstrap-datepicker/css/bootstrap-datepicker.css">
<input type="text" id="dt" class="form-control" placeholder="入职日期">
<script src="static/js/jquery-3.6.0.min.js"></script>
<script src="static/plugins/bootstrap-3.4.1/js/bootstrap.js"></script>
<script src="static/plugins/bootstrap-datepicker/js/bootstrap-datepicker.js"></script>
<script src="static/plugins/bootstrap-datepicker/locales/bootstrap-datepicker.zh-CN.min.js"></script>
<script>
$(function () {
$('#dt').datepicker({
format: 'yyyy-mm-dd',
startDate: '0',
language: "zh-CN",
autoclose: true
});
})
</script>
(1)首先,引入插件
在母版那修改一下,使子板可以自定义引入css、js
{% block css %}{% endblock %}
{% block js %}{% endblock %}
子板:
无论用不用ModelForm都要引入插件
(2)实现效果
具体去改子板内容时会遇到两种情况:
- 使用ModelForm
- 未使用ModelForm
- 如果没使用ModelForm,指定“id”即可使用我们写的js函数
- 若使用了ModelForm,怎么引入id呢?
我们发现,使用 ModelForm 的 id 会有一个规律,其 id = "id_" + "name"
那就简单了,直接改一下之前写的 js函数 就好了
效果展示:
4、ModelForm和BootStrap
我们先回顾一下之前ModelForm与BootStrap
的联系,后面再进一步优化我们的Code
-
(1)ModelForm可以帮助我们生成HTML标签
-
我们知道,ModelForm可以帮助我们生成HTML标签,但生成的标签没有 BootStrap 样式
class UserModelForm(forms.ModelForm): class Meta: model = models.UserInfo fields = ["name", "password",] form = UserModelForm()
{{form.name}} 普通的input框 {{form.password}} 普通的input框
-
(2)定义插件
我们通过定义插件,为HTML标签引入 BootStrap 样式
class UserModelForm(forms.ModelForm): class Meta: model = models.UserInfo fields = ["name", "password",] widgets = { "name": forms.TextInput(attrs={"class": "form-control"}), "password": forms.PasswordInput(attrs={"class": "form-control"}), "age": forms.TextInput(attrs={"class": "form-control"}), }
class UserModelForm(forms.ModelForm): name = forms.CharField( min_length=3, label="用户名", widget=forms.TextInput(attrs={"class": "form-control"}) ) class Meta: model = models.UserInfo fields = ["name", "password", "age"]
{{form.name}} BootStrap的input框 {{form.password}} BootStrap的input框
-
(3)重新定义的init方法,批量设置
一个一个引入 BootStrap 样式太繁琐了,所以后来我们重新定义的init方法,批量设置
class UserModelForm(forms.ModelForm): class Meta: model = models.UserInfo fields = ["name", "password", "age",] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 循环ModelForm中的所有字段,给每个字段的插件设置 for name, field in self.fields.items(): field.widget.attrs = { "class": "form-control", "placeholder": field.label }
class UserModelForm(forms.ModelForm): class Meta: model = models.UserInfo fields = ["name", "password", "age",] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 循环ModelForm中的所有字段,给每个字段的插件设置 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 }
class UserEditModelForm(forms.ModelForm): class Meta: model = models.UserInfo fields = ["name", "password", "age",] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 循环ModelForm中的所有字段,给每个字段的插件设置 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 }
我们发现,我们重复的写了一堆init方法,每次用ModelForm都要重写一遍,太繁琐了,咋整呢???
-
自定义类
utils/bootstrap.py
from django import forms
class BootStrap:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 循环ModelForm中的所有字段,给每个字段的插件设置
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时继承
class BootStrapModelForm(BootStrap, forms.ModelForm):
pass
# 使用Form时继承
class BootStrapForm(BootStrap, forms.Form):
pass
这样 我们写 ModelForm 就可以通过【继承】,省却不少功夫
from app01 import models
from app01.utils.bootstrap import BootStrapModelForm
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
from django import forms
class MyForm(BootStrapModelForm):
name = forms.CharField(min_length=3, label="用户名")
class Meta:
model = models.UserInfo
fields = ["name", "password", "age", 'account', 'create_time', "gender", "depart"]
class MobileModelForm(BootStrapModelForm):
"""靓号管理"""
# 验证:方式1
mobile = forms.CharField(
label="手机号",
validators=[RegexValidator(r'^1[3-9]\d{9}$', "手机号格式错误")], # 通过正则表达式限制输入格式,1开头,然后3~9,然后再来就个数
# 当然这个正则表达式可以叠加,多重限制
)
class Meta:
model = models.MobileNum
fields = ["mobile", "price", "level", "status"]
# 验证:方式2
def clean_mobile(self):
txt_mobile = self.cleaned_data["mobile"]
exists = models.MobileNum.objects.filter(mobile=txt_mobile).exists()
if exists:
raise ValidationError("手机号已存在")
# 验证通过,用户输入的值返回
return txt_mobile
class MobileEditModelForm(BootStrapModelForm):
# mobile = forms.CharField(disabled=True, label="手机号") # 设置手机号不可改变
class Meta:
model = models.MobileNum
fields = ['mobile', 'price', 'level', 'status']
def clean_mobile(self):
print(self.instance.pk) # 当前编辑的哪一行的ID
txt_mobile = self.cleaned_data["mobile"]
exists = models.MobileNum.objects.exclude(id=self.instance.pk).filter(mobile=txt_mobile).exists()
if exists:
raise ValidationError("手机号已存在")
return txt_mobile
5、整理文件
进行到现在,我们的文件数和代码量也越来也越大,需要规范一下了
- 提取公共的类
- 提取公共的类
- 视图函数的归类
- 等等
最终效果:
-
views/user.py
from django.shortcuts import render, redirect from app01 import models from app01.utils.form import MyForm def user_list(request): """用户管理""" # 获取所有的用户列表 queryset = models.UserInfo.objects.all() for obj in queryset: print(obj.id, obj.name, obj.account, obj.create_time.strftime("%Y-%m-%d"), type(obj.create_time)) # 想要id获取关联的数据 # 方式一: 传统方法,根据id获取关联的数据 temp = models.Department.objects.filter(id=obj.depart_id).first() print(temp.title) # 方式二: Django的方法(注意区别obj.depart_id与obj.depart.title) print(obj.depart_id) # 获取数据库中存储的那个字段值 print(obj.depart.title) # 根据id自动去关联的表中获取哪一行数据depart对象 return render(request, 'user_list.html', {"queryset": queryset}) def user_add(request): if request.method == "GET": form = MyForm() return render(request, 'user_add.html', {"form": form}) # 用户POST提交数据,数据效验(不能为空) # 如果数据合法,提交到数据库 form = MyForm(data=request.POST) if form.is_valid(): print(form.cleaned_data) # 将数据存储至数据库 # 方法一:老套路 # models.UserInfo.objects.create(...) # 方法二:ModelForm form.save() return redirect("/user/list/") # else: # print(form.errors) return render(request, 'user_add.html', {"form": form}) def user_edit(request, nid): """编辑用户""" if request.method == "GET": # 根据ID获取数据库中数据 row_object = models.UserInfo.objects.filter(id=nid).first() form = MyForm(instance=row_object) # 使用 instance ,ModelForm就可以默认将数据显示出来 return render(request, 'user_edit.html', {"form": form}) row_object = models.UserInfo.objects.filter(id=nid).first() # 这行代码勿忘,否则就成新增用户为不是编辑用户了 form = MyForm(data=request.POST, instance=row_object) if form.is_valid(): # 默认保存的是用户输入的所有数据,如果想要再用户输入以外增加一点值 # form.instance.字段名 = 值 如:form.age = 20 form.save() return redirect('/user/list/') # 如果数据不合格,则返回"user_edit.html" return render(request, 'user_edit.html', {"form": form}) def user_detele(request, nid): """删除用户""" models.UserInfo.objects.filter(id=nid).delete() return redirect("/user/list/")
-
views/depart.py
from django.shortcuts import render, redirect from app01 import models def depart_list(request): """部门列表""" # 去数据库中获取所有的部门列表 # 通过导入模块的方式直接获取所有表,存入queryset,省的表多的时候一个一个导入太麻烦了 queryset = models.Department.objects.all() # queryset 形式就是一个一个对象 [对象,对象,对象] return render(request, "depart_list.html", {'queryset': queryset}) def depart_add(request): """ 添加部门 """ # (1)处理GET请求 # 在部门聊表界面点击”新建部门“ ==> 提交GET请求(去 depart_add.html) if request.method == "GET": return render(request, 'depart_add.html') # (2)获得用户POST提交过来的数据 title = request.POST.get("title") """ 遗留问题: 输入为空怎么办?输入合不合法?等等 """ # (3)保存到数据库 models.Department.objects.create(title=title) # (4)重定向回部门列表 return redirect("/depart/list/") def depart_detele(request): """删除部门""" # 获取ID # 'https//127.0.0.1:8000/depart/delete/?nid=1' nid = request.GET.get("nid") # 删除 models.Department.objects.filter(id=nid).delete() # 重定向回部门列表 return redirect("/depart/list/") def depart_edit(request, nid): """修改部门""" if request.method == "GET": # 根据nid,获得他的数据 row_object = models.Department.objects.filter(id=nid).first() print(row_object.id, row_object.title) return render(request, 'depart_edit.html', {'row_object': row_object}) # 获取用户提交的标题 title = request.POST.get("title") # 根据ID找到数据库中的数据(部门名称),并更新 models.Department.objects.filter(id=nid).update(title=title) # 重定向回部门列表 return redirect("/depart/list/")
-
views/mobile.py
from django.shortcuts import render, redirect from app01 import models from app01.utils.pagination import Pagination from app01.utils.form import MobileModelForm, MobileEditModelForm def mobile_list(request): """ 靓号列表 """ data_dict = {} search_result = request.GET.get('q', "") # 如果有值就返回“q”,如果没有就返回空“” if search_result: data_dict["mobile__contains"] = search_result queryset = models.MobileNum.objects.filter(**data_dict).order_by("-level") page_object = Pagination(request, queryset) context = { "search_data": search_result, "queryset": page_object.page_queryset, # 分完页的数据 "page_string": page_object.html() # 页码 } return render(request, 'mobile_list.html', context) def mobile_add(request): """"添加靓号""" if request.method == "GET": form = MobileModelForm() return render(request, "mobile_add.html", {"form": form}) form = MobileModelForm(data=request.POST) if form.is_valid(): form.save() return redirect("/mobile/list/") return render(request, 'mobile_add.html', {"form": form}) def mobile_edit(request, nid): """编辑用户""" row_object = models.MobileNum.objects.filter(id=nid).first() # 这行代码勿忘,否则就成新增用户为不是编辑用户了 if request.method == "GET": # 根据ID获取数据库中数据 form = MobileEditModelForm(instance=row_object) # 使用 instance ,ModelForm就可以默认将数据显示出来 return render(request, 'mobile_edit.html', {"form": form}) form = MobileEditModelForm(data=request.POST, instance=row_object) if form.is_valid(): form.save() return redirect('/mobile/list/') # 如果数据不合格,则返回"user_edit.html" return render(request, 'mobile_edit.html', {"form": form}) def mobile_detele(request, nid): """删除用户""" models.MobileNum.objects.filter(id=nid).delete() return redirect("/mobile/list/")
-
utils/bootstrap.py
from django import forms
class BootStrap:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 循环ModelForm中的所有字段,给每个字段的插件设置
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时继承
class BootStrapModelForm(BootStrap, forms.ModelForm):
pass
# 使用Form时继承
class BootStrapForm(BootStrap, forms.Form):
pass
- utils/form.py
from app01 import models
from app01.utils.bootstrap import BootStrapModelForm
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
from django import forms
class MyForm(BootStrapModelForm):
name = forms.CharField(min_length=3, label="用户名")
class Meta:
model = models.UserInfo
fields = ["name", "password", "age", 'account', 'create_time', "gender", "depart"]
class MobileModelForm(BootStrapModelForm):
"""靓号管理"""
# 验证:方式1
mobile = forms.CharField(
label="手机号",
validators=[RegexValidator(r'^1[3-9]\d{9}$', "手机号格式错误")], # 通过正则表达式限制输入格式,1开头,然后3~9,然后再来就个数
# 当然这个正则表达式可以叠加,多重限制
)
class Meta:
model = models.MobileNum
fields = ["mobile", "price", "level", "status"]
# 验证:方式2
def clean_mobile(self):
txt_mobile = self.cleaned_data["mobile"]
exists = models.MobileNum.objects.filter(mobile=txt_mobile).exists()
if exists:
raise ValidationError("手机号已存在")
# 验证通过,用户输入的值返回
return txt_mobile
class MobileEditModelForm(BootStrapModelForm):
# mobile = forms.CharField(disabled=True, label="手机号") # 设置手机号不可改变
class Meta:
model = models.MobileNum
fields = ['mobile', 'price', 'level', 'status']
def clean_mobile(self):
print(self.instance.pk) # 当前编辑的哪一行的ID
txt_mobile = self.cleaned_data["mobile"]
exists = models.MobileNum.objects.exclude(id=self.instance.pk).filter(mobile=txt_mobile).exists()
if exists:
raise ValidationError("手机号已存在")
return txt_mobile
-
utils/pagination.py
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中传递的获取分页的参数,例如:/etty/list/?page=12 :param plus: 显示当前页的 前或后几页(页码) """ from django.http.request import QueryDict import copy query_dict = copy.deepcopy(request.GET) query_dict._mutable = True self.query_dict = query_dict self.page_param = page_param page = request.GET.get(page_param, "1") if page.isdecimal(): page = int(page) else: page = 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: # 数据库中的数据比较少,都没有达到11页。 start_page = 1 end_page = self.total_page_count else: # 数据库中的数据比较多 > 11页。 # 当前页<5时(小极值) if self.page <= self.plus: start_page = 1 end_page = 2 * self.plus + 1 else: # 当前页 > 5 # 当前页+5 > 总页面 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 + 1): 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())) search_string = """ <li> <form style="float: left;margin-left: -1px" method="get"> <input name="page" style="position: relative;float:left;display: inline-block;width: 80px;border-radius: 0;" type="text" class="form-control" placeholder="页码"> <button style="border-radius: 0" class="btn btn-default" type="submit">跳转</button> </form> </li> """ page_str_list.append(search_string) page_string = mark_safe("".join(page_str_list)) return page_string
-
urls.py
from django.contrib import admin from django.urls import path from app01.views import depart, user, mobile urlpatterns = [ path('depart/list/', depart.depart_list), path('depart/add/', depart.depart_add), path('depart/detele/', depart.depart_detele), path('depart/<int:nid>/edit/', depart.depart_edit), path('user/list/', user.user_list), path('user/add/', user.user_add), path('user/<int:nid>/edit/', user.user_edit), path("user/<int:nid>/detele/", user.user_detele), path("mobile/list/", mobile.mobile_list), path("mobile/add/", mobile.mobile_add), path('mobile/<int:nid>/edit/', mobile.mobile_edit), path("mobile/<int:nid>/detele/", mobile.mobile_detele), ]
更改后该有的效果无误