我们先设计一下靓号管理后台的表结构,如下图所示:
根据表结构的需求,在models.py中创建类(由类生成数据库中的表)。
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)
Django命令生成库表
python manage.py makemigrations
python manage.py migrate
新增对应的 增删改查对应的URL,如下图:
主要的增删改查的逻辑都写在views.py里面
views.py
# ################################# 靓号管理 #################################
from app01.utils.pagination import Pagination
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
def pretty_list(request):
""" 靓号列表 """
data_dict = {}
search_data = request.GET.get('q', "")
if search_data:
data_dict["mobile__contains"] = search_data
queryset = models.PrettyNum.objects.filter(**data_dict).order_by("-level")
page_object = Pagination(request, queryset)
context = {
"search_data": search_data,
"queryset": page_object.page_queryset, # 分完页的数据
"page_string": page_object.html() # 页码
}
return render(request, 'pretty_list.html', context)
class BootStrapModelForm(forms.ModelForm):
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 PrettyModelForm(BootStrapModelForm):
# 验证:方式1
mobile = forms.CharField(
label="手机号",
validators=[RegexValidator(r'^1[3-9]\d{9}$', '手机号格式错误'), ],
)
class Meta:
model = models.PrettyNum
# fields = "__all__"
# exclude = ['level']
fields = ["mobile", 'price', 'level', 'status']
# 验证:方式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
class PrettyEditModelForm(BootStrapModelForm):
# mobile = forms.CharField(disabled=True, label="手机号")
mobile = forms.CharField(
label="手机号",
validators=[RegexValidator(r'^1[3-9]\d{9}$', '手机号格式错误'), ],
)
class Meta:
model = models.PrettyNum
fields = ['mobile', 'price', 'level', 'status']
# 验证:方式2
def clean_mobile(self):
# 当前编辑的哪一行的ID
# print(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("手机号已存在")
# 验证通过,用户输入的值返回
return txt_mobile
def pretty_add(request):
""" 添加靓号 """
if request.method == "GET":
form = PrettyModelForm()
return render(request, 'pretty_add.html', {"form": form})
form = PrettyModelForm(data=request.POST)
if form.is_valid():
form.save()
return redirect('/pretty/list/')
return render(request, 'pretty_add.html', {"form": form})
def pretty_edit(request, nid):
""" 编辑靓号 """
row_object = models.PrettyNum.objects.filter(id=nid).first()
if request.method == "GET":
form = PrettyEditModelForm(instance=row_object)
return render(request, 'pretty_edit.html', {"form": form})
form = PrettyEditModelForm(data=request.POST, instance=row_object)
if form.is_valid():
form.save()
return redirect('/pretty/list/')
return render(request, 'pretty_edit.html', {"form": form})
def pretty_delete(request, nid):
models.PrettyNum.objects.filter(id=nid).delete()
return redirect('/pretty/list/')
因为靓号一般比较多,此时我们列表页面需要分页进行展示
一般我们在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 %}
{{ page_string }}
"""
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('- 首页
'.format(self.query_dict.urlencode()))
# 上一页
if self.page > 1:
self.query_dict.setlist(self.page_param, [self.page - 1])
prev = '- 上一页
'.format(self.query_dict.urlencode())
else:
self.query_dict.setlist(self.page_param, [1])
prev = '- 上一页
'.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 = '- {}
'.format(self.query_dict.urlencode(), i)
else:
ele = '- {}
'.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 = '- 下一页
'.format(self.query_dict.urlencode())
else:
self.query_dict.setlist(self.page_param, [self.total_page_count])
prev = '- 下一页
'.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('- 尾页
'.format(self.query_dict.urlencode()))
search_string = """
-
"""
page_str_list.append(search_string)
page_string = mark_safe("".join(page_str_list))
return page_string
pretty_list.html
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div style="margin-bottom: 10px" class="clearfix">
<a class="btn btn-success" href="/pretty/add/">
<span class="glyphicon glyphicon-plus-sign" aria-hidden="true">span>
新建靓号
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>
form>
div>
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>IDth>
<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="/pretty/{{ obj.id }}/edit/">编辑a>
<a class="btn btn-danger btn-xs" href="/pretty/{{ obj.id }}/delete/">删除a>
td>
tr>
{% endfor %}
tbody>
table>
div>
<div class="clearfix">
<ul class="pagination">
{{ page_string }}
ul>
div>
div>
{% endblock %}
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 %}
<button type="submit" class="btn btn-primary">提 交button>
form>
div>
div>
div>
{% endblock %}
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 %}
<button type="submit" class="btn btn-primary">提 交button>
form>
div>
div>
div>
{% endblock %}
简单的增删改查此处就不再验证了,我们来验证下分页的效果
MySQL 8.0开始支持with语句递归,此处我们直接使用with递归来造测试数据
insert into app01_prettynum(mobile, price, level, status)
with RECURSIVE c(n) as
(select 1 union all select n + 1 from c where n < 400)
select 13600000001 + n ,
ceil(100*rand()),
mod(n,4) + 1,
mod(n,2) + 1
from c;
首先进入到首页:
选择指定页面:
尾页