1创建模型
from django.db import models
# Create your models here.
class Department(models.Model):
def __str__(self):
return self.title
"""部门表"""
# id=models.BigAutoField(verbose_name="ID",primary_key=True)
# id=models.BigAutoField(verbose_name="ID",primary_key=True)可以自己写id,上面是bigint,下面是int,verbosename是表字段的注释
title=models.CharField(verbose_name="标题",max_length=32)
class UserInfo(models.Model):
"""员工表"""
name=models.CharField(verbose_name="姓名",max_length=16)
password=models.CharField(max_length=64)
age=models.IntegerField()
account=models.DecimalField(verbose_name="账户余额",max_digits=10,decimal_places=2,default=0)
create_time=models.DateField(verbose_name="入职时间")
#如果有外键
#无约束
# depart = models.BigIntegerField()
# 1有约束约束
#to与什么表关联
#to_field与表中那一列关联
#2 django自动
#写的是depart,实际上生成的是depart_id
#3部门表被删除
#3.1如果级联删除
depart = models.ForeignKey(to="Department", to_field="id",on_delete=models.CASCADE)
#3.2置空
#如果想要置空,首先需要允许为空
# depart = models.ForeignKey(to="Department", to_field="id",blank=True,null=True,on_delete=models.SET_NULL)
gender_choices=(
(1,"男"),
(2,"女")
)
gender=models.SmallIntegerField(choices=gender_choices,verbose_name="性别")
class PrettyNum(models.Model):
"""靓号表"""
mobile=models.CharField(verbose_name="手机号",max_length=32)#会用到搜索所以字符串
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)
插入数据
insert into app01_prettynum(mobile,price,level,status) values("1111111",55,1,1),("22222222",100,2,2),("33333333",120,4,2);
在这里插入代码片
2靓号列表
这个很简单
def pretty_list(request):
queryset = models.PrettyNum.objects.all().order_by("-level") # 按照level降序排序
return render(request, "pretty_list.html", {"queryset": queryset})
html
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div>
<a class="btn btn-success" href="/pretty/add/"><!--可以在新页面打开,只需要添加一个target="_blank"-->
<span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建靓号
</a>
</div>
<div class="panel panel-default" style="margin-top: 10px">
<div class="panel-heading">
<h3 class="panel-title"><span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>靓号列表
</h3>
</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>
{# 模版语法中不需要加括号 |date:"Y-m-d H:i:s"#}
<td>{{ obj.id }}</td>
<td>{{ obj.mobile }}</td>
<td>{{ obj.price }}</td>
<td>{{ obj.get_level_display }}</td>
{#去掉括号#}
<td>{{ obj.get_status_display }}</td>
<td>
<a href="/user/{{ obj.id }}/edit/" class="btn btn-primary btn-xs">编辑</a>
<a href="/user/{{ obj.id }}/delete/" class="btn btn-danger btn-xs">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}
3新建靓号
1列表点击跳转
2url
3modelform类
from django import forms
class PrettyModelForm(forms.ModelForm):
4函数
实例化类对象,render将对象传入html,循环展示所有字段
5点击提交
校验
保存
跳转回列表
这里需要处理mobile为非数字的问题
方式1字段+正则
from django.core.validators import RegexValidator
mobile=forms.CharField(
label="手机号",
validators=[RegexValidator(r'^1[3-9]\d{9}$','手机号格式错误')],
)
方法2钩子方法
def clean_mobile(self):
txt_mobile=self.cleaned_data
if len(txt_mobile) !=11:
raise ValidationError("格式错误")
return txt_mobile
上面的前提是
class Meta:
model = models.PrettyNum
# fields = "__all__"
fields=["mobile","price","level","status"]中的fields里面有mobile
。
编辑
1编辑列表
2url
3views
根据id获取当前编辑对象
modelform配合,默认显示数据
提交修改
- 修改靓号的时候很少需要对电话号码进行修改,所以用来编辑的类需要进行修改。下面注释行的作用就是防止mobile字段被修改
class PrettyEditModelForm(forms.ModelForm):
# mobile=forms.CharField(disabled=True)
不允许手机号重复
obj = models.PrettyNum.objects.filter(mobile=).exists()判断查询结果是否为空
这个功能可以加在钩子函数里面
def clean_mobile(self):
txt_mobile = self.cleaned_data["mobile"]
if models.PrettyNum.objects.filter(mobile=txt_mobile).exists():
raise ValidationError("已存在")
return txt_mobile
但是编辑提交的时候,手机号一定是存在的,应该是判断排除自己以外是否重复
- 比较简单的检查可以实用正则表达式,复杂的需要用钩子函数,编辑的完整代码如下
class PrettyEditModelForm(forms.ModelForm):
# mobile=forms.CharField(disabled=True)
mobile = forms.CharField(
label="手机号",
validators=[RegexValidator(r'^1[3-9]\d{9}$', '手机号格式错误')],
)
class Meta:
model = models.PrettyNum
# fields = "__all__"
fields=["mobile","price","level","status"]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for name, field in self.fields.items():
print(field, name)
field.widget.attrs = {"class": "form-control"}
def clean_mobile(self):
txt_mobile = self.cleaned_data["mobile"]
if models.PrettyNum.objects.exclude(id=self.instance.pk).filter(mobile=txt_mobile).exists():
raise ValidationError("已存在")
return txt_mobile
def pretty_edit(request,eid):
row_object=models.PrettyNum.objects.filter(id=eid).first()
if request.method=='GET':
form = PrettyEditModelForm(instance=row_object)
return render(request,"pretty_edit.html", {"form":form})
else:
form = PrettyEditModelForm(data=request.POST, instance=row_object) # 这里不能再是直接save了,需要加一个instance,否则就是直接添加了一行
if form.is_valid():
# 默认是用户输入的所有数据,如果有不希望用户输入且希望保存的
# form.instance.字段名=123
form.save()
return redirect("/pretty/list/")
else:
# print(form.errors)
return render(request, "pretty_edit.html", {"form": form}) # 这次这个form和get返回的是不一样的,包含了错误信息
搜索手机号
models.PrettyNum.objects.filter(mobile="19999",id=2)
也可以串一个字典
data_dict={"mobile":"19999","id":2}
models.PrettyNum.objects.filter(**data_dict)
models.PrettyNum.objects.filter(id=12)
models.PrettyNum.objects.filter(id__gt=12)
models.PrettyNum.objects.filter(id__gte=12)
models.PrettyNum.objects.filter(id__lt=12)
models.PrettyNum.objects.filter(mobile__startswith="12")
models.PrettyNum.objects.filter(mobile__endswith="12")
models.PrettyNum.objects.filter(mobile__contains="12")
也可以搞成字典
data_dict为空则相当于筛选条件为空,如果不为空就返回筛选后剩下的数据
def pretty_list(request):
data_dict={}
serrch_data = request.GET.get("q","")#用于获取 GET 请求中的查询参数 "q" 的值,如果不存在则返回默认值
if serrch_data:
data_dict={"mobile__contains":serrch_data}
# res=models.PrettyNum.objects.filter(**data_dict)
# print(res)
queryset = models.PrettyNum.objects.filter(**data_dict).order_by("-level") # 按照level降序排序
return render(request, "pretty_list.html", {"queryset": queryset,"serrch_data":serrch_data})
加载container和面板中间的一部分内容,添加了搜索输入框和搜索按钮,注意,button的type必须为submit,输入写在form表单中
<div class="clearfix">
<a class="btn btn-success" href="/pretty/add/"><!--可以在新页面打开,只需要添加一个target="_blank"-->
<span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建靓号
</a>
<div style="float: right;width: 300px">
<form action="" method="get">
<div class="input-group">
<input type="text" class="form-control" placeholder="{{ serrch_data }}" name="q">
<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>
分页
先添加300条数据
# for i in range(300):
# models.PrettyNum.objects.create(mobile="1223333333",price=10,level=1,status=2)
使用get请求处理分页,这种分页需要在url中输入页码
page=int(request.GET.get("page"))
start=(page-1)*10
end=start+10
if serrch_data:
data_dict={"mobile__contains":serrch_data}
queryset = models.PrettyNum.objects.filter(**data_dict).order_by("-level")[start:end] # 按照level降序排序
直接在后端生成html字符串,作为参数传入.html会直接显示字符串,除非标记为安全
from django.utils.safestring import mark_safe
for i in range(1,21):
ele='<li><a href="?page={}">{}</a></li>'.format(i,i)
page_str_list.append(ele)
page_string=mark_safe(("".join(page_str_list)))#需要标记为安全,否则无法将字符串转换为标签
data=models.PrettyNum.objects.all().acount()符合条件的个数
简单用if-else实现了页码的逻辑,可以输入页码进行跳转,下面是简单的代码
修改后的pretty_list.html
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div class="clearfix">
<a class="btn btn-success" href="/pretty/add/"><!--可以在新页面打开,只需要添加一个target="_blank"-->
<span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建靓号
</a>
<div style="float: right;width: 300px">
<form action="" method="get">
<div class="input-group">
<input type="text" class="form-control" placeholder="{{ serrch_data }}" name="q">
<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" style="margin-top: 10px">
<div class="panel-heading">
<h3 class="panel-title"><span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>靓号列表
</h3>
</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>
{# 模版语法中不需要加括号 |date:"Y-m-d H:i:s"#}
<td>{{ obj.id }}</td>
<td>{{ obj.mobile }}</td>
<td>{{ obj.price }}</td>
<td>{{ obj.get_level_display }}</td>
{#去掉括号#}
<td>{{ obj.get_status_display }}</td>
<td>
<a href="/pretty/{{ obj.id }}/edit/" class="btn btn-primary btn-xs">编辑</a>
<a href="/pretty/{{ obj.id }}/delete/" class="btn btn-danger btn-xs">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="clearfix">
<nav aria-label="Page navigation">
<ul class="pagination" style="float: left">
{{ page_string }}
<li>
<form action="" method="get" style="float: left">
<div class="input-group" style="width: 200px">
<input type="text" class="form-control" placeholder="页码" name="page">
<span class="input-group-btn">
<button class="btn btn-default" type="submit">跳转</button>
</span>
</div>
</form>
</li>
</ul>
</nav>
{# 这里新加了页码搜索的功能#}
</div>
</div>
{% endblock %}
views.py中修改后的pretty_list函数
def pretty_list(request):
data_dict={}
serrch_data = request.GET.get("q","")#用于获取 GET 请求中的查询参数 "q" 的值,如果不存在则返回默认值
# for i in range(300):生成数据
# models.PrettyNum.objects.create(mobile="1223333333",price=10,level=1,status=2)
# 符合数据的总条数
page = int(request.GET.get("page", 1))
page_size = 10
start = (page - 1) * page_size
end = start + page_size
page_str_list = []
if serrch_data:
data_dict={"mobile__contains":serrch_data}
#数据总条数
total_count = models.PrettyNum.objects.filter(**data_dict).order_by("-level").count()
#总页码
total_page_count, div = divmod(total_count, page_size)
if div:
total_page_count += 1
plus=5
if total_page_count<=plus*2+1:#数据库数据很少
start_page=1
end_page=total_page_count
else:
if page<=plus:
start_page=1
end_page=2*plus
elif plus+page>=total_page_count:
start_page = total_page_count-2*plus
end_page = total_page_count
else:
start_page=page-plus#数据很多
end_page=page+plus
if page>1:
prev = '<li><a href="?page={}">上一页</a></li>'.format(page-1)
else:
prev = '<li><a href="?page={}">上一页</a></li>'.format(1)
if page<total_page_count:
nxt = '<li><a href="?page={}">下一页</a></li>'.format(page+1)
else:
nxt = '<li><a href="?page={}">下一页</a></li>'.format(total_page_count)
page_str_list.append('<li><a href="?page={}">首页</a></li>'.format(1))
page_str_list.append(prev)
for i in range(start_page, end_page+1):
if i ==page:
ele = '<li class="active"><a href="?page={}">{}</a></li>'.format(i, i)
else:
ele = '<li><a href="?page={}">{}</a></li>'.format(i, i)
page_str_list.append(ele)
page_str_list.append(nxt)
page_str_list.append('<li><a href="?page={}">尾页</a></li>'.format(total_page_count))
page_string = mark_safe(("".join(page_str_list))) # 需要标记为安全,否则无法将字符串转换为标签
queryset = models.PrettyNum.objects.filter(**data_dict).order_by("-level")[start:end] # 按照level降序排序
return render(request, "pretty_list.html", {"queryset": queryset,"serrch_data":serrch_data,"page_string":page_string})
- 封装分页类
写项目用pagination.py公共组件
"""
自定义组件
自定义的分页组件,以后如果想要用这个分页组件,你需要做如下几件事:
def pretty_list(request):
#1根据自己的情况去筛选自己的数据
queryset = models.PrettyNum.objects.filter(**data_dict).order_by("-level")
#2实例化分页对象
page_obj=Pagenation(request,queryset)
context={
"queryset": page_obj.page_queryset,#分完页的数据
"page_string": page_obj.html()#生成页码
}
return render(request, "pretty_list.html", context)
在html中
数据:
{% for obj in queryset %}
<td>{{ obj.id }}</td>
{% endfor %}
页码:
<ul class="pagination" style="float: left">
{{ page_string }}
</ul>
"""
from django.utils.safestring import mark_safe
class Pagenation(object):
def __init__(self,request,queryset,page_size = 10,page_param="page",plus=5):
"""
:param request: 请求对象
:param queryset: 查询的符合条件的数据
:param page_size: 每页显示多少条数据
:param page_param: yrl中get的参数
:param plus: 当前页的前后多少个被显示
"""
page=request.GET.get(page_param,"1")
if page.isdecimal():
page=int(page)
else:page=1
self.page=page
# print(page,type(page))
self.page_size=page_size
start = (page - 1) * page_size
end = start + page_size
self.start=start
self.end=end
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
self.page_str_list = []
def html(self):
if self.total_page_count <= self.plus * 2 + 1: # 数据库数据很少
start_page = 1
end_page = self.total_page_count
else:
if self.page <= self.plus:
start_page = 1
end_page = 2 * self.plus
elif self.plus + self.page >= 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
if self.page > 1:
prev = '<li><a href="?page={}">上一页</a></li>'.format(self.page - 1)
else:
prev = '<li><a href="?page={}">上一页</a></li>'.format(1)
if self.page < self.total_page_count:
nxt = '<li><a href="?page={}">下一页</a></li>'.format(self.page + 1)
else:
nxt = '<li><a href="?page={}">下一页</a></li>'.format(self.total_page_count)
self.page_str_list.append('<li><a href="?page={}">首页</a></li>'.format(1))
self.page_str_list.append(prev)
for i in range(start_page, end_page + 1):
if i == self.page:
ele = '<li class="active"><a href="?page={}">{}</a></li>'.format(i, i)
else:
ele = '<li><a href="?page={}">{}</a></li>'.format(i, i)
self.page_str_list.append(ele)
self.page_str_list.append(nxt)
self.page_str_list.append('<li><a href="?page={}">尾页</a></li>'.format(self.total_page_count))
search_string = """<li>
<form action="" method="get" style="float: left">
<div class="input-group" style="width: 200px">
<input type="text" class="form-control" placeholder="页码" name="page">
<span class="input-group-btn">
<button class="btn btn-default" type="submit">跳转</button>
</span>
</div>
</form>
</li>"""
self.page_str_list.append(search_string)
page_string = mark_safe(("".join(self.page_str_list))) # 需要标记为安全,否则无法将字符串转换为标签
return page_string
def pretty_list(request):
# for i in range(300):生成数据
# models.PrettyNum.objects.create(mobile="1223333333",price=10,level=1,status=2)
serrch_data = request.GET.get("q", "") # 用于获取 GET 请求中的查询参数 "q" 的值,如果不存在则返回默认值
data_dict = {}
if serrch_data:
data_dict={"mobile__contains":serrch_data}
queryset = models.PrettyNum.objects.filter(**data_dict).order_by("-level")
page_obj=Pagenation(request,queryset)
context={
"queryset": page_obj.page_queryset,
"serrch_data": serrch_data,
"page_string": page_obj.html()
}
return render(request, "pretty_list.html", context)
还有一个小bug,搜索&分页
bootstrap样式父类
- 项目文件的整理
自定义一个类,放在app01的utils目录下,命名为bootstrap,然后将views里的UserModelForm,PrettyModelForm,PrettyEditModelForm三个类装在utils目录下的form.py文件中,form.py要导入bootstrap中的父类;再把user,pretty,depart分别装在app下的views目录下,把原来的views删掉,导入包。修改urls的导入和url路径,就完成了项目文件的整理。
class BootStrapModelForm(forms.ModelForm):
# mobile=forms.CharField(disabled=True)
class Meta:
model = models.PrettyNum
# fields = "__all__"
fields=["mobile","price","level","status"]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for name, field in self.fields.items():
# print(field, name)
#字段中有属性保留原来的属性,没有属性才增加
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}