def ab_pl(request):
book_list = []
for i in range(10000):
book_obj = modes.Book(name = '第%s本书' % i)
book_list.append(book_obj)
# 批量插入数据时, 使用bulk_create能够大大减少操作
models.Book.objects.bulk_create(book_list)
1. queryset对象是可以直接切片操作的,因此可以通过切片来控制数据的展出
2. 确定用户要访问那一页 url?page=1
使用page参数来获取
current_page = request.GET.get('page',1) # 默认是第一页
# 获取的数据都是字符串类型需要注意类型转换
3. 自己规定每页展示多少条数据
per_page_num = 10
4. 动态获取切片的起始和结束位置
start_page = (current_page - 1) * per_page_num
stop_page = current_page * per_page_num
5. 获取当前数据的总条数
all_count = book_queryset.count()
6. 确定总共需要多少页才能展示完所有的数据
# 利用python内置函数divmod()
page_count , more = divmod(all_count,per_page_num)
if more:
page_count += 1
7. 前端模板语法没有range功能
可以在后端写好了传递给页面
8. 针对需要展示的页码规划好到底展示多少个页码
# 一般情况下页码的个数设计都是奇数(符合审美标准) eg:11个页码
在当前页减5 加6 确保当前页处于中间位置
可以添加高亮显示
9. 针对页码小于6,处于最后6页的情况下 做特殊处理
注意事项:
在Django框架中,当我们使用非Django内置的第三方功能或者组件代码的时候
通常会建立一个utils文件夹,在该文件夹内对模块进行功能性划分,(utils可以在每个应用下创建,具体)结合实际情况
下方自定义分页器是基于bootstrap样式来的 所以需要你提前导入bootstrap
class Pagination(object):
def __init__(self, current_page, all_count, per_page_num=2, pager_count=11):
"""
封装分页相关数据
:param current_page: 当前页
:param all_count: 数据库中的数据总条数
:param per_page_num: 每页显示的数据条数
:param pager_count: 最多显示的页码个数
"""
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('''
)
first_page = '首页 ' % (1)
page_html_list.append(first_page)
if self.current_page <= 1:
prev_page = '上一页 '
else:
prev_page = '上一页 ' % (self.current_page - 1,)
page_html_list.append(prev_page)
for i in range(pager_start, pager_end):
if i == self.current_page:
temp = '%s ' % (i, i,)
else:
temp = '%s ' % (i, i,)
page_html_list.append(temp)
if self.current_page >= self.all_pager:
next_page = '下一页 '
else:
next_page = '下一页 ' % (self.current_page + 1,)
page_html_list.append(next_page)
last_page = '尾页 ' % (self.all_pager,)
page_html_list.append(last_page)
# 尾部添加标签
page_html_list.append('''
''')
return ''.join(page_html_list)
def add_book(request):
# 获取所有的book对象
book_queryset = models.Book.objects.all()
# 默认是第一页
current_page = request.GET.get('page',1)
# 统计有多少个book对象
all_count = book_queryset.count()
# 传值生成对象
page_obj = Pagination(current_page=current_page,all_count=all_count)
# 将page_queryset传递到页面
page_queryset = book_queryset[page_obj.start:page_obj.end]
return render(request, 'book_list.html',locals())
{% for book_obj in page_queryset %}
<p>{{ book_obj.title }}</p>
{% endfor %}
{{ page_obj.page_html|safe }}
forms组件主要功能是django针对前端form表单提交数据进行校验,也是用处最多的功能,另外也有着渲染html代码和展示提示信息另外两种功能
补充: 为什么数据校验非要去后端,不能在前端利用js直接完成?
# 写在views.py中
from django import forms
class MyForm(forms.Form):
# username字符串类型最小3位最大8位
username = forms.CharField(min_length=3,max_length=8)
# password字符串类型最小3位最大8位
password = forms.CharField(min_length=3,max_length=8)
# email字段必须符合邮箱格式 [email protected]
email = forms.EmailField()
from app01 import views
1 将带校验的数据组织成字典的形式传入
form_obj = views.MyForm({'username':'xxx','password':'123','email':'123'})
2 判断数据是否合法 (.is_valied) 注意该方法只有在所有的数据全部合法的情况下才会返回True
form_obj.is_valid() # False
3 查看所有校验通过的数据 (.cleaned_data)
form_obj.cleaned_data # {'username':'xxx','password':'123'}
4 查看所有不符合校验规则以及不符合的原因
form_obj.errors # {'email': ['Enter a valid email address.']}
5 校验数据只校验类中出现的字段 多传不影响 多传的字段直接忽略
form_obj = views.MyForm({'username':'xxx','password':'123','email':'[email protected]','hobby':'study'})
form_obj.is_valid() # True
6 校验数据 默认情况下 类里面所有的字段都必须传值
form_obj = views.MyForm({'username':'xxx','password':'123'})
form_obj.is_valid() # False
"""
也就意味着校验数据的时候 默认情况下数据可以多传但是绝不可能少传
"""
def index(request):
1. 先产生一个空对象
from_obj = MyForm()
2. 直接将该空对象传递给html页面
return render(request,'index.html',locals())
1. 第一种:代码书写极少 封装程度太高 不便于后续的扩展 一般情况下只在本地测试使用
{{ form_obj.as_p }}
{{ form_obj.as_ul }}
{{ form_obj.as_table}}
2. 第二种:可扩展性很强 但是需要书写的代码太多 一般情况下不用
<p>{{ form_obj.username.label }}:{{ form_obj.username }}</p>
<p>{{ form_obj.password.label }}:{{ form_obj.password }}</p>
<p>{{ form_obj.email.label }}:{{ form_obj.email }}</p>
3. 第三种:代码书写简单 并且扩展性也高
{% for form in form_obj %}
<p>{{ form.label }}:{{ form }}</p>
{% endfor %}
label属性默认展示的是类中定义的字段首字母大写的形式
也可以自己修改 直接给字段对象加label属性即可
username = forms.CharField(min_length=3,max_length=8,label=‘用户名’)
注意:
后端核心代码:
def index(request):
# 1. 先产生一个空对象
form_obj = MyForm()
if request.method == 'POST':
# 获取用户数据并校验
"""
数据校验注意事项:
1. 数据获取繁琐
2. 校验数据需要构造成字典的格式传入
ps: 但是request.POST 可以看成是一个字典
"""
# 3. 校验数据
form_obj = MyForm(request.POST)
# 4. 判断数据是否合法
if form_obj.is_valid():
# 5. 操作合法 数据存储数据库
return HttpResponse('OK')
# 2. 直接将该空对象传递给html页面
return render(request,'index.html',locals())
前端代码:
<form action="">
{% for form in form_obj %}
<p>{{ form.label }}:{{ form }}
<span style="color: red">{{ form.errors.0 }}</span>
</p>
{% endfor %}
<input type="submit" class="btn-info btn ">
</form>
展示效果:
补充: 上图效果是浏览器附带的展示错误信息,一般不会采用而是自己定制
让浏览器不做校验方法:
<form action="" method="post" novalidate> // 添加novalidate参数
自定制错误信息:
class MyForm(forms.Form):
# username字符串类型最小3位最大8位
username = forms.CharField(min_length=3,max_length=8,label='用户名',
error_messages={
'min_length':'用户名最少3位',
'max_length':'用户名最大8位',
'required':"用户名不能为空"
}
)
# password字符串类型最小3位最大8位
password = forms.CharField(min_length=3,max_length=8,label='密码',
error_messages={
'min_length': '密码最少3位',
'max_length': '密码最大8位',
'required': "密码不能为空"
}
)
# email字段必须符合邮箱格式 [email protected]
email = forms.EmailField(label='邮箱',
error_messages={
'invalid':'邮箱格式不正确',
'required': "邮箱不能为空"
}
)
钩子函数在forms组件中就类似于第二道关卡,能够让我们自定义校验规则
在特定的节点自动触发完成响应操作
在forms组件中有两类钩子
局部钩子
给单个字段增加校验规则的时候可以使用
全局钩子
当需要给多个字段增加校验规则的时候使用
# 局部钩子
def clean_username(self):
# 获取到用户名
username = self.cleaned_data.get('username')
if '666' in username:
# 提示前端展示错误信息
self.add_error('username','光喊666是不行滴~')
# 将钩子函数钩去出来数据再放回去
return username
# 全局钩子
def clean(self):
password = self.cleaned_data.get('password')
confirm_password = self.cleaned_data.get('confirm_password')
if not confirm_password == password:
self.add_error('confirm_password','两次密码不一致')
# 将钩子函数钩出来数据再放回去
return self.cleaned_data
label 字段名
error_messages 自定义报错信息
initial 默认值
required 控制字段是否必填
"""
1.字段没有样式
2.针对不同类型的input如何修改
text
password
date
radio
checkbox
...
"""
widget=forms.widgets.PasswordInput(attrs={'class':'form-control c1 c2'})
# 多个属性值的话 直接空格隔开即可
# 第一道关卡里面还支持正则校验
from django.core.validators import RegexValidator
validators=[
RegexValidator(r'^[0-9]+$', '请输入数字'),
RegexValidator(r'^159[0-9]+$', '数字必须以159开头')
]
# radio
gender = forms.ChoiceField(
choices=((1, "男"), (2, "女"), (3, "保密")),
label="性别",
initial=3,
widget=forms.widgets.RadioSelect()
)
# select
hobby = forms.ChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=3,
widget=forms.widgets.Select()
)
# 多选
hobby1 = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=[1, 3],
widget=forms.widgets.SelectMultiple()
)
# 单选checkbox
keep = forms.ChoiceField(
label="是否记住密码",
initial="checked",
widget=forms.widgets.CheckboxInput()
)
# 多选checkbox
hobby2 = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=[1, 3],
widget=forms.widgets.CheckboxSelectMultiple()
)