django学习笔记
今日内容概要
- 批量操作数据
- 自定义分页器
- form组件
- modelform组件
今日内容详细
批量操作数据
浏览器访问一个django路由,立刻创建10万条数据并展示到前端页面
低效率
涉及到大批量数据的创建直接使用create可能会造成数据库崩溃
def index(request):
for i in range(100000):
models.Book.objects.create(title=f'第{i}本书')
高效率
def index(request):
book_list = []
for i in range(100000):
# 先用类产生一个对象
book_obj = models.Book(title=f'第{i}本书')
# 将对象追加到列表中
book_list.append(book_obj)
'''上述四行可以简写为一行>>>:列表生成式(列表表达式)'''
models.Book.objects.bulk_create(book_list) # 批量创建数据
book_query = models.Book.objects.all()
return render(request,'bookList.html',locals())
"""
批量数据创建>>>:bulk_create()
批量数据修改>>>:bulk_update()
"""
自定义分页器
推导流程
当数据量比较大的时候网站不可能将所有的数据全部展示到一页,页面展示应该考虑分页
1.QuerySet对象支持切片操作
2.分页必需的四个参数(数学关系)
3.页码展示
如何根据总数据和每页展示的数据得出总页码
内置方法:divmod()
4.如何渲染出所有的页码标签
前端模板语法不支持range 但是后端支持 我们可以在后端创建好html标签然后传递给html页面使用
5.如何限制住展示的页面标签个数
页码推荐使用奇数位(对称美) 利用当前页前后固定位数来限制
6.首尾页码展示范围问题
"""
上述是分页器组件的推导流程我们无需真正编写
django自带一个分页器组件 但是不太好用 我们自己也写了一个
"""
自定义分页器使用
from app01.plugins import mypage
book_query = models.Book.objects.all()
# 产生分页器对象
page_obj =
mypage.Pagination(current_page=request.GET.get('page'),all_count=book_query.count())
# 产生分页数据对象
page_query = book_query[page_obj.start:page_obj.end]
return render(request, 'bookList.html', locals())
{% for book_obj in page_query %}
<p class="text-center">{{ book_obj.title }}</p>
{% endfor %}
{{ page_obj.page_html|safe }}
参考博客:https://www.cnblogs.com/Dominic-Ji/articles/12035722.html
form组件
前戏:编写用户登录功能并且校验数据返回提示信息(form表单)
1.前端需要自己编写获取用户数据的各种标签
2.前端需要自己想方设法的展示错误的提示信息
3.后端需要自己想方设法的编写校验代码(很多if判断)
def ab_form(request):
data_dict = {'username':'','password':''}
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'jason':
data_dict['username'] = 'jason是你能随便用的吗'
if password == '123':
data_dict['password'] = '密码就设123???'
return render(request,'ab_form.html',locals())
上面的问题form组件
可以一次性搞定:
1.数据校验:支持提前设置各种校验规则之后自动校验
2.渲染页面:支持直接渲染获取用户数据的各种标签
3.展示信息:支持针对不同的校验失败展示不同的提示
基本使用
from django import forms
class MyForm(forms.Form):
name = forms.CharField(max_length=8, min_length=3) # 用户名最长八个字符 最短三个字符
age = forms.IntegerField(max_value=150, min_value=0) # 年龄最小0岁 最大150岁
email = forms.EmailField() # 邮箱必须符合邮箱格式(至少有个@符号)
数据校验功能
from app01 import views
# 1.将数据传入实例化对象
form_obj = views.MyForm({'name':'jason','age':18,'email':123})
# 2.判断所有的数据是否符合校验(全部符合结果才是True)
form_obj.is_valid()
# 3.获取符合校验规则的数据
form_obj.claned_data
{'name': 'jason', 'age': 18}
# 4.查阅不符合校验规则的数据及错误原因
form_obj.errors
{'email': ['Enter a valid email address.']}
"""
1.forms类中所有的字段数据默认都是必填的 少传则肯定通不过校验
如果想忽略某些字段可以添加 required=False
2.forms类中额外传入的字段数据不会做任何的校验 直接忽略
"""
渲染标签功能
# 渲染方式1:封装程度高、扩展性较差 主要用于快速生成页面测试功能
{{ form_obj.as_p }}
{{ form_obj.as_table }}
{{ form_obj.as_ul }}
# 渲染方式2:封装程度低、扩展性较好 但是字段比较多的情况下不方便,编写困难
{{ form_obj.name.label }} # 文本提示
{{ form_obj.name }} # 获取用户数据的标签
# 渲染方式3:推荐使用!!!
{% for form in form_obj %}
<p>{{ form.label }}{{ form }}</p>
{% endfor %}
"""
1.forms组件只负责渲染获取用户数据的标签
form表单标签和提交按钮需要自己写
2.渲染标签中文提示 可以使用参数 label指定 不指定默认使用字段名首字母大写
"""
展示提示信息
form表单如何取消浏览器自动添加的数据校验功能(novalidate)
<form action="" method="post" novalidate>
{% for form in form_obj %}
<p>
{{ form.label }}{{ form }}
<span style="color: red;">{{ form.errors.0 }}</span> # 点零只拿错误信息固定文本
</p>
{% endfor %}
<input type="submit" value="提交">
def func(request):
form_obj = MyForm()
if request.method == 'POST':
form_obj = MyForm(request.POST)
if form_obj.is_valid():
form_obj.cleaned_data
return render(request,'func.html',locals())
提示信息可以自定义
username = forms.CharField(min_length=3,max_length=8,label='用户名',
error_messages={
'min_length':'用户名最短3位',
'max_length':'用户名最长8位',
'required':'用户名必填'
})
重要字段参数
参数 | 说明 |
---|---|
min_length | 最小长度 |
max_length | 最大长度 |
min_value | 最小值 |
max_value | 最大值 |
label | 字段注释 |
error_messages | 错误提示 |
initial | 默认值 |
validators | 正则校验 |
widget | 控制渲染出来的标签各项属性 |
choices | 选择类型的标签内部对应关系 |
required | 是否为空 |
validators案例
from django.core.validators import RegexValidator
phone = forms.CharField(
validators=[
RegexValidator(r'^[0-9]+$', '请输入数字'),
RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],
)
widget案例
# forms.widgets.控制type的类型(attrs=控制各项属性:class id ...)
password = forms.CharField(widget=forms.widgets.PasswordInput(attrs={'class':'form-control'})
)
钩子函数
钩子函数的作用就是提供自定义的校验方式
局部钩子(校验单个字段)
我们在Form类中定义clean_字段名()
方法,就能够实现对特定字段进行校验。
# 局部钩子:校验用户名是否已存在(一次性只能勾一个人)
'''钩子函数是数据经过了字段第一层参数校验之后才会执行'''
def clean_name(self): # 自动生成的函数名 专门用于对name字段添加额外的校验规则
# 1.先获取用户名
name = self.cleaned_data.get('name')
# 2.判断用户名是否已存在
res = models.User.objects.filter(name=name).first()
if res:
# 3.提示信息
return self.add_error('name', '用户名已存在')
# 4.最后将你勾上来的name返回回去
return name
全局钩子(校验多个字段)
我们在Form类中定义 clean()
方法,就能够实现对字段进行全局校验。
# 全局钩子:校验密码与确认密码是否一致(一次性可以勾多个人)
def clean(self):
# 1.获取多个字段数据
pwd = self.cleaned_data.get('pwd')
confirm_pwd = self.cleaned_data.get('confirm_pwd')
if not pwd == confirm_pwd:
return self.add_error('confirm_pwd', '两次密码不一致 ')
# 最后将整个数据返回
return self.cleaned_data
form组件源码分析
我们从is_valid如何校验数据进行分析。
第一步:查看类源码发现只要给类传参self.is_bound肯定是True,重点看self.errors
def is_valid(self):
return self.is_bound and not self.errors
第二步:查看了源码发现self._errors初始化就是None 所以肯定看full_clean方法
@property
def errors(self):
if self._errors is None:
self.full_clean()
return self._errors
第三步:查看full_clean方法,发现主要有3个方法。
def full_clean(self):
self.cleaned_data = {} # 创建了一个存储检查没问题的数据的空字典
self._clean_fields() # 校验数据
self._clean_form() # 封装
self._post_clean() # 返回
第四步:查看self._clean_fields方法
def _clean_fields(self):
for name, field in self.fields.items():# 循环获取字段名和字段对象
if field.disabled: # 字段是否是禁用的
value = self.get_initial_for_field(field, name)
else:
value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name)) # 如果不是禁用,获取字段对应的用户数据,进行校验
try: # 异常捕获
if isinstance(field, FileField): # 文件数据
initial = self.get_initial_for_field(field, name)
value = field.clean(value, initial)
else:
value = field.clean(value) # clean校验的方法
self.cleaned_data[name] = value # 以上校验没有问题,就上传到cleaned_data里
if hasattr(self, 'clean_%s' % name): # 利用反射判断是否拥有clean_%s的方法,就是在获取钩子函数执行,
value = getattr(self, 'clean_%s' % name)()
self.cleaned_data[name] = value # 钩子函数不报错,添加到cleaned_data里
except ValidationError as e: # 钩子函数报错,添加提示保存信息
self.add_error(name, e)
查看源码发现校验数据的整个过程内部都有异常处理机制
from django.core.exceptions import ValidationError
raise ValidationError('用户名不存在')
# 自己在clean_%s钩子函数主动抛出一个异常,也不会报错,因为钩子函数是交给_clean_fields这个方法里执行的,就算clean_%s报错也是交给了_clean_fields方法里的异常捕获处理。
modelform组件
modelform是form的优化版本使用更简单功能更强大,由于模型类里面的一张张表数据要经常校验,这时候modelform就诞生了,是专门针对模型类使用的。
基本使用
class MyModelForm(forms.ModelForm):
class Meta:
model = models.User # 指定对哪张表做数据校验
fields = '__all__' # 指定对所有字段做数据校验
labels = {} # 给每个字段加名字
widgets = {} # 给每个字段加属性
class Meta下常用参数
model = models.Book # 对应的Model中的类
fields = "__all__" # 字段,如果是__all__,就是表示列出所有的字段
exclude = None # 排除的字段
labels = None # 提示信息
help_texts = None # 帮助提示信息
widgets = None # 自定义插件
error_messages = None # 自定义错误信息
save()方法
每个ModelForm还具有一个save()
方法。 这个方法根据表单绑定的数据创建并保存数据库对象。 ModelForm的子类可以接受现有的模型实例作为关键字参数instance
;如果提供此功能,则save()将更新该实例。 如果没有提供,save() 将创建模型的一个新实例。
class MyModelForm(forms.ModelForm):
class Meta:
model = models.User
fields = '__all__'
def clean_name(self):
name = self.cleaned_data.get('name')
res = models.User.objects.filter(name=name).first()
if res:
self.add_error('name','用户名已存在')
return name
def md(request):
modelform_obj = MyModelForm()
if request.method == 'POST':
# edit_obj = models.User.objects.filter(name='jason').first()
# modelform_obj = MyModelForm(request.POST,instance=edit_obj) 获取修改对象之后再次使用save()方法是修改
modelform_obj = MyModelForm(request.POST)
if modelform_obj.is_valid():
modelform_obj.save() # 保存数据
return render(request,'ab.html',locals())
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了