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())
posted @   空白o  阅读(38)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示