先赞后看,养成习惯。。。
目录
数据库设置
基本功能
路由器
靓号显示
靓号添加
靓号编辑
视图函数
额外功能
搜索功能
分页
一般逻辑
动态页码
上下页
首尾页
数据库设置
新建一个数据库(或者就用之前部门、用户管理的也行),用Django连接到数据库:
DATABASES = {
"default": {
"ENGINE": "django.db.backends.mysql",
"NAME": "mydata_1",
"USER": "root",
"PASSWORD": "您的密码",
"HOST": "127.0.0.1",
"PORT": "3306",
}
}
新建数据库,在models中新建数据表,定义表的结构:
from django.db import models
# Create your models here.
class Vanitynumber(models.Model):
mobile = models.CharField(verbose_name="手机号",max_length=11, unique=True)
price = models.IntegerField(verbose_name="价格",default=0)
# 默认为空:null=True,blank=True
level_ = {
(1,"一类号"),
(2,"二类号"),
(3,"三类号"),
}
statues_ = {
(1,"占用"),
(2,"未占用"),
}
level = models.SmallIntegerField(verbose_name="级别",choices=level_,default=1)
status = models.SmallIntegerField(verbose_name="占用情况",choices=statues_,default=2)
迁移数据库命令:
# 生成迁移文件
python manage.py makemigrations
# 应用迁移
python manage.py migrate
可以自己添加点数据并在终端查看是否存入数据库:
insert into app01_vanitynumber(mobile,price,level,status) values(13992349299,2400,2,2);
与预期无异。
基本功能
大体跟之前用户、部门管理类似,大家也可以玩玩其他的BootStrap样式
路由器
from django.contrib import admin
from django.urls import path
from sqlalchemy.dialects.mssql.information_schema import views
from app01 import views
urlpatterns = [
path("admin/", admin.site.urls),
path("", views.vanity_show),
path("vanity/show/", views.vanity_show),
path("vanity/add/", views.vanity_add),
path("vanity/<int:nid>/edit/", views.vanity_edit),
path("vanity/<int:nid>/delete/", views.vanity_delete),
]
靓号显示
vanity_show.html:
{% extends 'layout.html' %}
{% block content %}
<div class="panel panel-default" style="margin-left: 40px;margin-right: 40px">
<div class="panel-heading" style="color: #1265b5">靓号列表</div>
<a href="/vanity/add/">
<button type="button" class="btn btn-success" style="margin-top: 10px;margin-left: 10px">
<span class="glyphicon glyphicon-pencil" aria-hidden="true" ></span>
添加靓号</button>
</a>
<div class="panel-body">
<div class="bs-example" data-example-id="bordered-table">
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th>ID</th>
<th>号码</th>
<th>价格</th>
<th>类别</th>
<th>占用情况</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for items in queryset %}
<tr>
<th scope="row">{
{ items.id }}</th>
<td>{
{ items.mobile }}</td>
<td>{
{ items.price }}</td>
<td>{
{ items.get_level_display }}</td>
<td>{
{ items.get_status_display }}</td>
<td>
<a class="btn btn-primary btn-xs" href="/vanity/{
{ items.id }}/edit/">编辑</a>
<a class="btn btn-danger btn-xs" href="/vanity/{
{ items.id }}/delete/">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}
靓号添加
vanity_add.html:
{% extends 'layout.html' %}
{% block content %}
<div class="container-fluid" style="margin-top: 10px">
<div class="my-div">
<div class="container">
<div class="panel panel-default" style="width: 750px;margin-top: 10px">
<!-- Default panel contents -->
<div class="panel-heading" style="margin-top: 0">新建 靓号</div>
<div class="panel-body">
<form class="form-horizontal" method="POST" action="/vanity/add/">
{% csrf_token %}
<!-- 输入框 -->
{% for items in form %}
<div class="form-group">
<label class="col-sm-2 control-label">{
{ items.label }}</label>
<div class="col-sm-5">
{# <input type="text" class="form-control" id="inputDepartmentName" placeholder="" name="{
{ items.label }}">#}
{
{ items }}
{# 要是有错误信息,会显示#}
<span style="color: red">{
{ items.errors.0 }}</span>
{# 直接手写{
{ items }},因为他会自动帮我们生成html标签#}
</div>
</div>
{# {
{ items.label }} : {
{ items }}#}
{% endfor %}
<!-- 提交按钮 -->
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">提 交</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
靓号编辑
vanity_edit.html:
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div class="panel panel-default" style="width: 750px;margin-top: 10px">
<div class="panel-heading" style="margin-top: 0">编辑 靓号</div>
<div class="panel-body">
<form class="form-horizontal" method="POST" >
{% csrf_token %}
<!-- 输入框 -->
{% for items in form %}
<div class="form-group">
<label for="inputDepartmentName" class="col-sm-2 control-label">{
{ items.label }}</label>
<div class="col-sm-5">
{
{ items }}
<span style="color: red">{
{ items.errors.0 }}</span>
</div>
</div>
{% endfor %}
<!-- 提交按钮 -->
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">提 交</button>
</div>
</div>
</form>
</div>
</div>
</div>
{% endblock %}
视图函数
from django.shortcuts import render,HttpResponse,redirect
from app01.models import Vanitynumber
from django import forms
class VanitynumberForm(forms.ModelForm):
class Meta:
model = Vanitynumber
fields = ['mobile','price','level','status']
# 2、循环到所有的插件再赋予form-control样式
def __init__(self,*args,**kwargs):
super(VanitynumberForm,self).__init__(*args,**kwargs)
# 循环找到所有的插件
for name,field in self.fields.items():
field.widget.attrs = {'class':'form-control'}
def vanity_show(request):
form = VanitynumberForm()
queryset = Vanitynumber.objects.all()
return render(request,'vanity_show.html',{'form':form,'queryset':queryset})
def vanity_add(request):
"""添加用户,ModelForm版本"""
if request.method == "GET":
form = VanitynumberForm()
return render(request, "vanity_add.html",{"form":form})
# 用户提交POST请求,校验数据
form = VanitynumberForm(data=request.POST)
if form.is_valid():
form.save()
return redirect("/vanity/show")
else:
print(form.errors)
def vanity_edit(request,nid):
row_object = Vanitynumber.objects.filter(id=nid).first()
if request.method == "GET":
# 根据ID去数据库获取要编辑的那一行数据(对象)
# 加上instance=row_object会默认将对应ID的数据显示到页面输入框中,这就比之前的部门管理要方便很多
form = VanitynumberForm(instance=row_object)
return render(request, "vanity_edit.html", {"form": form})
# 如果不加下面这行代码,并且UserForm中加instance,那么数据库中就不是更新数据,因为程序不知道要更新哪一行数据,所有程序会自动新添加一行数据,原来数据保持不变
form = VanitynumberForm(data=request.POST, instance=row_object)
if form.is_valid():
form.save()
return redirect("/vanity/show/")
return render(request, "vanity_edit.html", {"form": form})
def vanity_delete(request,nid):
Vanitynumber.objects.filter(id=nid).delete()
return redirect("/vanity/show/")
额外功能
搜索功能
搜索功能就是按照输入的某些数字,在已有号码中搜索满足某些条件的内容
Django中搜索的两张方式:
都是差不多的,区别在于第二种传字典作为参数
第一种:
q1 = Vanitynumber.objects.filter(status=1,id=1)
print(q1)
第二种:
data_list = {"mobile":"19999234567","id":3}
q2 = Vanitynumber.objects.filter(**data_list)
print(q2)
使用第二种更方便做其他操作,比如加正则。
Django还提供了一些更加便捷的索引规则,比如双下划线,可将具体筛选规则以字典形式传参,前面要加上双星号。
Vanitynumber.objects.filter(id=12) # 等于12
Vanitynumber.objects.filter(id__gt=12) # 大于12
Vanitynumber.objects.filter(id__gte=12) # 大于等于12
Vanitynumber.objects.filter(id__lt=12) # 小于12
Vanitynumber.objects.filter(id__lte=12) # 小于等于12
data_dict1 = {"id__lte":12}
Vanitynumber.objects.filter(**data_dict1)
Vanitynumber.objects.filter(mobile="999") # 等于
Vanitynumber.objects.filter(mobile__startswith="1999") # 筛选出以1999开头
Vanitynumber.objects.filter(mobile__endswith="222") # 筛选出以222结尾
Vanitynumber.objects.filter(mobile__contains="222") # 筛选出包含222
data_dict2 = {"mobile__contains":"222"}
Vanitynumber.objects.filter(**data_dict2)
在展示页面,我们可以在右上方加个输入框和提交按钮,用户接收用户搜索输入语句,有时用户输入可能包含其他字符,我们可以用正则提取到符合条件的内容,这里是连续的数字。通过data_list搜索到的数据,可以返还到html上,显示搜索到的内容。具体实现代码如下:
views.py:
import re
def vanity_show(request):
form = VanitynumberForm()
# 先定义一个壳子,再添加
data_list = {}
search_data = request.GET.get('q',"")
search_data = re.search(r'\d+', search_data)
if search_data:
search_data=search_data.group()
else:
search_data=""
if search_data:
data_list["mobile__contains"] = search_data
queryset = Vanitynumber.objects.filter(**data_list).order_by('-level')
return render(request,'vanity_show.html',{'form':form,'queryset':queryset,'search_data':search_data})
这里通过GET传参获取q和一个空字符串,作用是使当q无值时,默认为空,以避免因data_list无值而产生报错;对search_data运行正则提取第一个出现的连续数字,如果不为空,则解对象,如果为空,赋值空字符串;再将search_data添加到search_list中;通过filter找到符合条件的queryset,传回html
vanity_show.html:
{% extends 'layout.html' %}
{% block content %}
<div class="panel panel-default" style="margin-left: 40px;margin-right: 40px">
<div class="panel-heading" style="color: #1265b5">靓号列表</div>
<a href="/vanity/add/">
<button type="button" class="btn btn-success" style="margin-top: 10px;margin-left: 10px">
<span class="glyphicon glyphicon-pencil" aria-hidden="true" ></span>
添加靓号</button>
</a>
<div style="float: right;width: 300px">
<div class="input-group" style="display: flex; align-items: center;">
<form action="" method="GET" style="display: flex; align-items: center;">
<label style="margin-top: 11px;margin-right: 4px;margin-bottom: 11px">
<input type="text" name="q" class="form-control" placeholder="Search" value="{
{ search_data }}" style="margin-right: 4px;">
</label>
<span class="input-group-btn">
<button class="btn btn-primary" type="submit">
<span class="glyphicon glyphicon-search" aria-hidden="true"></span>
<span class="glyphicon-class">搜索</span>
</button>
</span>
</form>
</div>
</div>
<div class="panel-body">
<div class="bs-example" data-example-id="bordered-table">
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th>ID</th>
<th>号码</th>
<th>价格</th>
<th>类别</th>
<th>占用情况</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for items in queryset %}
<tr>
<th scope="row">{
{ items.id }}</th>
<td>{
{ items.mobile }}</td>
<td>{
{ items.price }}</td>
<td>{
{ items.get_level_display }}</td>
<td>{
{ items.get_status_display }}</td>
<td>
<a class="btn btn-primary btn-xs" href="/vanity/{
{ items.id }}/edit/">编辑</a>
<a class="btn btn-danger btn-xs" href="/vanity/{
{ items.id }}/delete/">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}
再将符合条件信息展示到页面上,并将输入框传入search_data作为默认值。
这样,就完成了搜索功能。
分页
一般逻辑
当数据量很大的时候,页面会向下延伸很长,这样不仅影响观感,还浪费空间,可以设置分页功能,为每一页都设置一个最大数据量。
分页的具体逻辑也很简单,也是html标签实现跳转:
<li><a href="/vanity/show/?page={i}"></a></li>
但如果页数很多的话,html文件会很长,而且也不利于一些更复杂的操作,可以在视图函数中以字符串的形式传给html页面,但直接传的话会报错,Django无法正常将字符串标签转换为标签,需要在视图函数中引入mark_safe函数,对字符串进行包裹,类似这样:
mark_safe('<li><a href="/vanity/show/?page={i}"></a></li>')
也可将一个一个的标签,动态存入列表,再使用join函数构造字符串,再用mark_safe包裹,传回html,例如:
data_list = []
for i in range(start_page,end_page+1):
if i == page:
str_html_1 = f'<li class="active"><a href="/vanity/show/?page={i}">{i}</a></li>'
else:
str_html_1 = f'<li><a href="/vanity/show/?page={i}">{i}</a></li>'
data_list .append(str_html_1)
data_str = mark_safe("\n".join(data_list ))
queryset = Vanitynumber.objects.filter(**data_list).order_by('-level')[start:end]
对于多行数据,一页是放不下的,可以一页放一定的数据(这里一页放10条),放多页。
此时,对于第一页:页数是1,数据是(0,10)
第二页:页数是2,数据是(10,20)
...............
第 n 页:页数是n,数据是((n-1)×10,n×10)
数据库中的数据条数是可以用函数得到的:
num = Vanitynumber.objects.all().count()
所以可通过总数据条数反推页数
对于特定的某页,可通过索引在数据库中取值:
start = (page-1)*10
end = page * 10
queryset = Vanitynumber.objects.filter(**data_list).order_by('-level')[start:end]
再构造标签字符串,使得页面中点击页码就会跳转到该页,并显示数据。
动态页码
但如果数据量足够多,页码也会很多,在页面中也显示不完,或者说看着很丑,这时,可用动态页码加以改进:
我们希望在页面上展示的页码数不能超过一定的阈值(这里是11)。对于用户提交,从而前端返回的页码数page,我们可用只显示page前五页到page后五页,总共11页
即:
start_page = page - 5
end_page = page + 5
这样去显示效果会好得多,但这样还要问题,当用户点击4页及以前或者最大页码时,左边会出现负数页码,右边则可能会报错,如何解决呢?
可以增加判断语句,如果页码 <= 5,start_page = 1,如果页码 >= 最大页码 - 4,end_page = 最大页码:
if start_page < 1:
start_page = 1
end_page = page + 5
# 保证页码不会超限
if end_page > int(Vanitynumber.objects.all().count()/10):
end_page = int(Vanitynumber.objects.all().count()/10)
这样,就能保证不会出现异常。
上下页
增加上下页逻辑很简单,就是点击后page相应地加一减一。需要注意的是“上一页”应当放到最前面,"下一页"放到最后面,因为.append()方法是在后面添加,当然,你也可以使用其他的插入函数。
并且,当page已经是首页或者尾页时,不能跳转了,需要判断一下,具体代码如下:
# 上一页
if page >= 1:
prev = f'<li><a href="/vanity/show/?page={page-1}">上一页</a></li>'
page_html_list.append(prev)
else:
prev = f'<li><a href="/vanity/show/?page=1">上一页</a></li>'
page_html_list.append(prev)
# 下一页
if page < int(Vanitynumber.objects.all().count() / 10):
prior = f'<li><a href="/vanity/show/?page={page + 1}">下一页</a></li>'
page_html_list.append(prior)
else:
prior = f'<li><a href="/vanity/show/?page={int(Vanitynumber.objects.all().count() / 10)}">下一页</a></li>'
page_html_list.append(prior)
首尾页
首尾页直接定位即可:
# 首页
page_html_list.append(f'<li><a href="/vanity/show/?page={1}">首页</a></li>')
# 尾页
page_html_list.append(f'<li><a href="/vanity/show/?page={int(Vanitynumber.objects.all().count() / 10)}">尾页</a></li>')
分页相关代码如下:
views.py:
vanity_show(request):
form = VanitynumberForm()
data_list = {}
search_data = request.GET.get('q',"")
search_data = re.search(r'\d+', search_data)
if search_data:
search_data=search_data.group()
else:
search_data=""
if search_data:
data_list["mobile__contains"] = search_data
page = int(request.GET.get('page',1))
page_html_list = []
# 如果展示页码大于10
if int(Vanitynumber.objects.all().count()/10) > 10:
# 起止页码
start = (page-1)*10
end = page*10
# 页码轮转
start_page = page - 5
# 保证页码不会出现非正数
if start_page < 1:
start_page = 1
end_page = page + 5
# 保证页码不会超限
if end_page > int(Vanitynumber.objects.all().count()/10):
end_page = int(Vanitynumber.objects.all().count()/10)
# 首页
page_html_list.append(f'<li><a href="/vanity/show/?page={1}">首页</a></li>')
# 上一页
if page >= 1:
prev = f'<li><a href="/vanity/show/?page={page-1}">上一页</a></li>'
page_html_list.append(prev)
else:
prev = f'<li><a href="/vanity/show/?page=1">上一页</a></li>'
page_html_list.append(prev)
# 构造标签
for i in range(start_page,end_page+1):
if i == page:
str_html_1 = f'<li class="active"><a href="/vanity/show/?page={i}">{i}</a></li>'
else:
str_html_1 = f'<li><a href="/vanity/show/?page={i}">{i}</a></li>'
page_html_list.append(str_html_1)
# 下一页
if page < int(Vanitynumber.objects.all().count() / 10):
prior = f'<li><a href="/vanity/show/?page={page + 1}">下一页</a></li>'
page_html_list.append(prior)
else:
prior = f'<li><a href="/vanity/show/?page={int(Vanitynumber.objects.all().count() / 10)}">下一页</a></li>'
page_html_list.append(prior)
# 尾页
page_html_list.append(f'<li><a href="/vanity/show/?page={int(Vanitynumber.objects.all().count() / 10)}">尾页</a></li>')
# mark_safe使得字符串能在页面正常显示
data_str = mark_safe("\n".join(page_html_list))
queryset = Vanitynumber.objects.filter(**data_list).order_by('-level')[start:end]
# 展示页码小于10
else:
start = (page - 1) * 10
end = page * 10
num = int(Vanitynumber.objects.all().count()/10)
for i in range(1, num+1):
if i == page:
str_html_2 = f'<li class="active"><a href="/vanity/show/?page={i}">{i}</a></li>'
else:
str_html_2 = f'<li><a href="/vanity/show/?page={i}">{i}</a></li>'
page_html_list.append(str_html_2)
data_str = mark_safe("\n".join(page_html_list))
queryset = Vanitynumber.objects.filter(**data_list).order_by('-level')[start:end]
return render(request,'vanity_show.html',{'form':form,'queryset':queryset,'search_data':search_data,'data_str':data_str})
vanity_show:
{% extends 'layout.html' %}
{% block content %}
<div class="panel panel-default" style="margin-left: 40px;margin-right: 40px">
<div class="panel-heading" style="color: #1265b5">靓号列表</div>
<a href="/vanity/add/">
<button type="button" class="btn btn-success" style="margin-top: 10px;margin-left: 10px">
<span class="glyphicon glyphicon-pencil" aria-hidden="true" ></span>
添加靓号</button>
</a>
<div style="float: right;width: 300px">
<div class="input-group" style="display: flex; align-items: center;">
<form action="" method="GET" style="display: flex; align-items: center;">
<label style="margin-top: 11px;margin-right: 4px;margin-bottom: 11px">
<input type="text" name="q" class="form-control" placeholder="Search" value="{
{ search_data }}" style="margin-right: 4px;">
</label>
<span class="input-group-btn">
<button class="btn btn-primary" type="submit">
<span class="glyphicon glyphicon-search" aria-hidden="true"></span>
<span class="glyphicon-class">搜索</span>
</button>
</span>
</form>
</div>
</div>
<div class="panel-body">
<div class="bs-example" data-example-id="bordered-table">
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th>ID</th>
<th>号码</th>
<th>价格</th>
<th>类别</th>
<th>占用情况</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for items in queryset %}
<tr>
<th scope="row">{
{ items.id }}</th>
<td>{
{ items.mobile }}</td>
<td>{
{ items.price }}</td>
<td>{
{ items.get_level_display }}</td>
<td>{
{ items.get_status_display }}</td>
<td>
<a class="btn btn-primary btn-xs" href="/vanity/{
{ items.id }}/edit/">编辑</a>
<a class="btn btn-danger btn-xs" href="/vanity/{
{ items.id }}/delete/">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<ul class="pagination">
{
{ data_str }}
</ul>
</div>
{% endblock %}
因为用了ModelForm,无序列表中直接{ { data_str }}即可,无需循环。
需要整个项目源码的可以私信我,这里我就不放出来了,过几天会放到我的资源里,大家自取。
感谢您的三连!!!