分页
view层
def fenye(request):
all_data = models.AuthorDetail.objects.all()
current_page = request.GET.get('page',1)
count = all_data.count()
page_obj = Pagination(current_page=current_page,all_count=count)
data = all_data[page_obj.start:page_obj.end]
return render(request,'fenye.html',locals())
模板层
{% for a in data %}
<p>{{ a.addr }}</p>
{% endfor %}
{{ page_obj.page_html|safe }}
自定义分页器
class Pagination(object):
def __init__(self, current_page, all_count, per_page_num=10, pager_count=11):
"""
封装分页相关数据
:param current_page: 当前页
:param all_count: 数据库中的数据总条数
:param per_page_num: 每页显示的数据条数
:param pager_count: 最多显示的页码个数
用法:
queryset = model.objects.all()
page_obj = Pagination(current_page,all_count)
page_data = queryset[page_obj.start:page_obj.end]
获取数据用page_data而不再使用原始的queryset
获取前端分页样式用page_obj.page_html
"""
try:
current_page = int(current_page)
except Exception as e:
current_page = 1
if current_page < 1:
current_page = 1
self.current_page = current_page
self.all_count = all_count
self.per_page_num = per_page_num
# 总页码
all_pager, tmp = divmod(all_count, per_page_num)
if tmp:
all_pager += 1
self.all_pager = all_pager
self.pager_count = pager_count
self.pager_count_half = int((pager_count - 1) / 2)
@property
def start(self):
return (self.current_page - 1) * self.per_page_num
@property
def end(self):
return self.current_page * self.per_page_num
def page_html(self):
# 如果总页码 < 11个:
if self.all_pager <= self.pager_count:
pager_start = 1
pager_end = self.all_pager + 1
# 总页码 > 11
else:
# 当前页如果<=页面上最多显示11/2个页码
if self.current_page <= self.pager_count_half:
pager_start = 1
pager_end = self.pager_count + 1
# 当前页大于5
else:
# 页码翻到最后
if (self.current_page + self.pager_count_half) > self.all_pager:
pager_end = self.all_pager + 1
pager_start = self.all_pager - self.pager_count + 1
else:
pager_start = self.current_page - self.pager_count_half
pager_end = self.current_page + self.pager_count_half + 1
page_html_list = []
# 添加前面的nav和ul标签
page_html_list.append('''
<nav aria-label='Page navigation>'
<ul class='pagination'>
''')
first_page = '<li><a href="?page=%s">首页</a></li>' % (1)
page_html_list.append(first_page)
if self.current_page <= 1:
prev_page = '<li class="disabled"><a href="#">上一页</a></li>'
else:
prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,)
page_html_list.append(prev_page)
for i in range(pager_start, pager_end):
if i == self.current_page:
temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,)
else:
temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,)
page_html_list.append(temp)
if self.current_page >= self.all_pager:
next_page = '<li class="disabled"><a href="#">下一页</a></li>'
else:
next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,)
page_html_list.append(next_page)
last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,)
page_html_list.append(last_page)
# 尾部添加标签
page_html_list.append('''
</nav>
</ul>
''')
return ''.join(page_html_list)
form表单校验组件
由于校验的安全性,前端可以没有校验,但是后端必须校验
自定义组件
from django import forms
from app01 import models
class myzx(forms.Form):
username = forms.CharField(min_length=3, max_length=8, label='用户名',
error_messages={
'min_length': '用户名不能少于三位',
'max_length': '用户名不能大于八位',
'required': '用户名不能为空',
}, widget=forms.widgets.TextInput(attrs={'class': 'form-control'})
)
password = forms.CharField(min_length=3, max_length=8, label='密码',
error_messages={
'min_length': '密码不能少于三位',
'max_length': '密码不能大于八位',
'required': '密码不能为空',
}, widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'})
)
confirm_password = forms.CharField(min_length=3, max_length=8, label='确认密码',
error_messages={
'min_length': '确认密码不能少于三位',
'max_length': '确认密码不能大于八位',
'required': '确认密码不能为空',
}, widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'})
)
email = forms.EmailField(
label='邮箱',
error_messages={
'required': '邮箱不能为空',
'invalid': '邮箱格式不正确'
}, widget=forms.widgets.EmailInput(attrs={"class": 'form-control'})
)
前端表单可以自己写,也可以使用组件提供的
obj为自己定义的组件对象
<form method="POST">
<p>{{ obj.username }}</p>
<p>{{ obj.confirm_password }}</p>
<p>{{ obj.confirm_password }}</p>
<p>{{ obj.email }}</p>
<input type="submit" value="提交">
</form>
但是这样渲染,会默认加上前端的校验,但是加在前端是不安全的,所以不采用
<form method="POST">
<p><input type="text" name="username" class="form-control" maxlength="8" minlength="3" required="" id="id_username"></p>
<p><input type="password" name="confirm_password" class="form-control" maxlength="8" minlength="3" required="" id="id_confirm_password"></p>
<p><input type="password" name="confirm_password" class="form-control" maxlength="8" minlength="3" required="" id="id_confirm_password"></p>
<p><input type="email" name="email" class="form-control" required="" id="id_email"></p>
<input type="submit" value="提交">
</form>
要在form表单上加上novalidate,可以取消上面的功能
<form method="POST" novalidate>
校验上传的数据
#验证数据,获取验证结果对象
form_obj = myforms.MyRegForm(request.POST)
#判断是否全部验证成功
if form_obj.is_valid():
#用户提交的数据(字典形式的字段和字段值)
print(form_obj.cleaned_data)
else:
#用户提交错误的数据(错误信息为带前端样式的错误字段和错误描述信息)
#是一个字典,上面的打印信息时重写了str的原因
print(form_obj.errors)
半完成版小案例
前端
obj.errors.username.0,才能拿到不带前端标签,完整的错误描述
<form method="POST" novalidate>
<p>{{ obj.username.label }}{{ obj.username }}{{ obj.errors.username.0 }}</p>
<p>{{ obj.password.label }}{{ obj.password }}{{ obj.errors.password.0 }}</p>
<p>{{ obj.confirm_password.label }}{{ obj.confirm_password }}{{ obj.errors.confirm_password.0 }}</p>
<p>{{ obj.email.label }}{{ obj.email }}{{ obj.errors.email.0 }}</p>
<input type="submit" value="提交">
</form>
后端
可以通过这个简便的实现数据提交,form的内容还在,因为obj记录下来数据了
其实后端返回的都是obj对象,只是最开始的那个是没有数据的,所以主要起渲染界面用
将数据传上去后,生成的那个对象是带数据的,所以会携带错误信息并渲染到前端,两个obj是不同的
def text(request):
if request.method == 'GET':
obj = myforms.myzx()
return render(request,'zx.html',locals())
else:
obj = myforms.myzx(request.POST)
if obj.is_valid():
#打印验证成功的数据,即使数据有错也能打印,只打印验证通过的
print("验证成功",obj.cleaned_data)
return HttpResponse("数据校验成功")
else:
print("验证失败",obj.errors)
print(type(obj.errors))
return render(request,'zx.html',locals())
forms数据添加
在写forms约束的时候我们可以把字段名和models里面涉及的一样,这样的话,clean_data的数据字典就完全符合提交数据的要求了
#这样的话,数据就不用一条一条的取出来了,可以一次性提交数据
if obj.is_valid():
models.books.objects.create(**obj.cleaned_data)
forms数据编辑
编辑界面需要元数据的内容,那么就可以使用校验功能,然后把数据返回前台,而且数据是从数据库拿去的数据是不会有问题的,前端的样式和add差不多
def edit_user(request,nid):
if request.method == 'GET':
#获取数据
data = models.UserInfo.objects.filter(pk=nid).first()
#传给forms,因为它可以自动生成html
obj = UserForm({'username':data.username,'email':data.email})
return render(request,'edit_user.html',locals())
#else为修改数据的请求
else:
obj = UserForm(request.POST)
if obj.is_valid():
models.UserInfo.objects.filter(pk=id).update(**obj.cleaned_data)
return redirect('/users/')
else:
return render(request,'edit_user.html')
form字段大全
https://www.cnblogs.com/xiaoyuanqujing/articles/11753466.html
其中weight是生成前端代码的重要插件
返回参数为数字的单选框
zx = fields.ChoiceField(
choices = [(1,'钱'),(2,'权')]
)
动态获取元数据
一
刷新界面会执行这些代码,相当于重新去数据库取值
hobby = forms.IntegerField(
label='爱好',
widget=widgets.Select()
)
def __init__(self,*args,**kwargs):
super(myzx,self).__init__(*args,**kwargs)
print(models.Blog.objects.values_list('id','site_title'))
#注意这句要写后面,否则会被super覆盖掉值
self.fields['hobby'].widget.choices = models.Blog.objects.values_list('id','site_title')
二
from django.forms.models import ModelChoiceField
hobby2 = ModelChoiceField(
label='爱好2',
#是显示的数据部分,想要指定显示内容需要去重写models的str方法
queryset=models.Blog.objects.all(),
#只是显示value值,上传是使用的
to_field_name='id'
)
from前端html生成简写-高级
但是不推荐,自定制比较弱
{{obj.as_p}}
<ul>
{{obj.as_ul}}
</ul>
<table>
{{obj.as_table}}
</table>
前端字符串转标签
from django.utils safestring import mark_safe
txt = mark_safe(txt)
钩子函数
注意这些函数都要写在form类中
局部钩子函数
给部分字段加强校验
def clean_username(self):
username = self.cleaned_data.get('username')
if '88' in username:
self.add_error('username','名字不能包含88')
return username
全局钩子函数
针对多个字段校验使用全局钩子函数
def clean(self):
password = self.cleaned_data.get('password')
re_password = self.cleaned_data.get('re_password')
if not password == re_password:
self.add_error('re_password','密码不相等啊')
return self.cleaned_data
正则和其他表单数据提交
#
# phone = forms.CharField(label='手机号',validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')])
#
#
#
# """下面的是了解知识点 你只需要整理到你的博客中 到时候需要用 直接来拷贝即可"""
#
# gender = forms.ChoiceField(
# choices=((1, "男"), (2, "女"), (3, "保密")),
# label="性别",
# initial=3,
# widget=widgets.RadioSelect()
# )
#
#
#
#
# hobby = forms.ChoiceField(
# choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
# label="爱好",
# initial=3,
# widget=widgets.Select()
# )
#
# hobby1 = forms.MultipleChoiceField(
# choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
# label="爱好",
# initial=[1, 3],
# widget=widgets.SelectMultiple()
# )
#
# keep = forms.ChoiceField(
# label="是否记住密码",
# initial="checked",
# widget=forms.widgets.CheckboxInput()
# )
#
# hobby2 = forms.MultipleChoiceField(
# choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
# label="爱好",
# initial=[1, 3],
# widget=forms.widgets.CheckboxSelectMultiple()
# )