django 的批量操作数据 和 form组件

django 的批量操作数据 和 form组件

目录

  • 批量操作数据
  • 自定义分页器
  • form组件
  • modelfrom组件

批量数据操作

批量数据前期准备

  • 访问django路由,创建数据并显示到前端页面

    利用create()方法创建数据到表中,利用all()方法进行查看

    def index(request):
        for i in range(100000):
           models.Book.objects.create(title=f'第{i}本书')
        
        book_query = models.Book.objects.all() # 将插入的数据再查询出来
        return render(request,'bookList.html',locals())
    

    利用for循环存储,直接查看数据,再数据额较大的时候,容易导致网页卡顿,甚至造成数据库系统的崩溃

  • 解决数据额较大,数据库系统崩溃的办法

    可以使用bulk_create( ) 方法 批量创建数据,使用bulk_update()方法 批量修改数据

    创建数据的时候可以使用一个列表存储,一并创建

    def index(request):
        book_list = []
        
        for i in range(100000):
            book_obj = models.Book(title=f'第{i}本书')
            book_list.append(book_obj)
        '''
        上述四行可以简写为一行>>>:列表生成式(列表表达式)
        book_list= [models.Book(title=f'第{i}本书') for i in range(100000) ]
        '''
        
        models.Book.objects.bulk_create(book_list)  # 批量创建数据
        book_query = models.Book.objects.all()
        return render(request,'bookList.html',locals())
    

分页器

当数据量比较大的时候 页面展示应该考虑分页

分页器组件的推到流程

  1. 先由queryset做切片操作

  2. 分页的样式的添加

  3. 页码展示

    ​ 如何根据总数据和每页展示的数据得出总页码

    ​ 这里需要用到内置方法divmod()

    divmod() 方法就是整除取余
    如:
    divmod(100,10)
    (10,0)  # 10页
    divmod(101,10)
    (10,1) 	# 11页
    divmod(99,10)
    (9,9)   # 10页
    

    ​ 余数只要不是0就需要在第一个数字上加一

  4. 渲染出所有的页码标签

    ​ 前端模板语法不支持range 但是后端支持 我们可以在后端创建好html标签然后传递给html页面使用

  5. 限制住展示的页面标签个数

    ​ 页码推荐使用奇数位(对称美) 利用当前页前后固定位数来限制

  6. 首尾页码展示范围问题

    ​ 最小值为首页,最大值为尾页

自定义分页器

自定义分页器的封装代码

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('''
                    <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)

自定义分页器的使用方法

  1. 后端

     def get_book(request):
       book_list = models.Book.objects.all()
       current_page = request.GET.get("page",1)
       all_count = book_list.count()
       page_obj = Pagination(current_page=current_page,all_count=all_count,per_page_num=10)
       page_queryset = book_list[page_obj.start:page_obj.end]
       return render(request,'booklist.html',locals())
    
  2. 前端

    <div class="container">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                {% for book_obj in page_queryset %}
                <p class="text-center">{{ book_obj.title }}</p>
                {% endfor %}
                {{ page_obj.page_html|safe }}
            </div>
        </div>
    </div>
    

form 组件

form组件的简介

form 组件就是再html页面中利用from表单向后端提交数据,对用户所输入的数据与后端的数据相校验

form组件是Django中自带的一个功能,使用之前需要提前导入模块

form组件的功能

  1. 数据校验

    ​ 支持提前设置各种校验规则 之后自动校验

  2. 渲染页面

    ​ 支持直接渲染获取用户数据的各种标签

  3. 展示信息

    ​ 支持针对不同的校验失败展示不同的提示

form类型的创建

  • 创建form类型的函数

     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()  # 邮箱必须符合邮箱格式(至少有个@符号)
    
  • 数据的校验功能

    1.1.传递待校验的数据
    form_obj = views.MyForm({'name':'jason','age':18,'email':123})
    1.2.判断所有的数据是否符合校验
    form_obj.is_valid()
    1.3.获取符合校验规则的数据
    form_obj.cleaned_data
    {'name': 'jason', 'age': 18}
    1.4.查阅不符合校验规则的数据及错误原因
    form_obj.errors
    {'email': ['Enter a valid email address.']}
    

    1.form类中编写的字段默认都是必填的 少传则肯定通不过校验 is_valid
    2.校验如果多传了一些字段 则不参与校验 全程忽略

  • 渲染标签功能

    2.1.方式1(封装程度高 扩展性差)
    {{ form_obj.as_p }}
    {{ form_obj.as_table }}
    {{ form_obj.as_ul }}
    2.2.方式2(封装程度低 扩展性好 编写困难)
    {{ form_obj.name.lable }}
    {{ form_obj.name }}
    2.3.方式3(推荐使用)
    {% for form in form_obj %}
    <p>{{ form.label }}{{ form }}</p>
    {% endfor %}
    

    类中以外的所有标签都不会自动渲染 需要自己编写

  • 展示提示信息

    form表单如何取消浏览器自动添加的数据校验功能
    <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():
                print(form_obj.cleaned_data)
                return render(request,'func.html',locals())
    

重要字段参数

重要字段参数 作用
max_length 最大长度
min_length 最小长度
max_value 最大值
min_value 最小值
label 字段注释
error_messages 错误提示
required 是否为空
widget 标签类型、标签属性
initial 默认值
validators 正则校验

注意:

  1. 使用正则校验的时候需要提前导入模块

    from django.core.validators import RegexValidator
    
  2. widget可在字段中直接修改默认的css 样式

  • 字段参数详情

    Field
        required=True,               是否允许为空
        widget=None,                 HTML插件
        label=None,                  用于生成Label标签或显示内容
        initial=None,                初始值
        help_text='',                帮助信息(在标签旁边显示)
        error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'}
        validators=[],               自定义验证规则
        localize=False,              是否支持本地化
        disabled=False,              是否可以编辑
        label_suffix=None            Label内容后缀
     
     
    CharField(Field)
        max_length=None,             最大长度
        min_length=None,             最小长度
        strip=True                   是否移除用户输入空白
     
    IntegerField(Field)
        max_value=None,              最大值
        min_value=None,              最小值
     
    FloatField(IntegerField)
        ...
     
    DecimalField(IntegerField)
        max_value=None,              最大值
        min_value=None,              最小值
        max_digits=None,             总长度
        decimal_places=None,         小数位长度
     
    BaseTemporalField(Field)
        input_formats=None          时间格式化   
     
    DateField(BaseTemporalField)    格式:2015-09-01
    TimeField(BaseTemporalField)    格式:11:12
    DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
     
    DurationField(Field)            时间间隔:%d %H:%M:%S.%f
        ...
     
    RegexField(CharField)
        regex,                      自定制正则表达式
        max_length=None,            最大长度
        min_length=None,            最小长度
        error_message=None,         忽略,错误信息使用 error_messages={'invalid': '...'}
     
    EmailField(CharField)      
        ...
     
    FileField(Field)
        allow_empty_file=False     是否允许空文件
     
    ImageField(FileField)      
        ...
        注:需要PIL模块,pip3 install Pillow
        以上两个字典使用时,需要注意两点:
            - form表单中 enctype="multipart/form-data"
            - view函数中 obj = MyForm(request.POST, request.FILES)
     
    URLField(Field)
        ...
     
     
    BooleanField(Field)  
        ...
     
    NullBooleanField(BooleanField)
        ...
     
    ChoiceField(Field)
        ...
        choices=(),                选项,如:choices = ((0,'上海'),(1,'北京'),)
        required=True,             是否必填
        widget=None,               插件,默认select插件
        label=None,                Label内容
        initial=None,              初始值
        help_text='',              帮助提示
     
     
    ModelChoiceField(ChoiceField)
        ...                        django.forms.models.ModelChoiceField
        queryset,                  # 查询数据库中的数据
        empty_label="---------",   # 默认空显示内容
        to_field_name=None,        # HTML中value的值对应的字段
        limit_choices_to=None      # ModelForm中对queryset二次筛选
         
    ModelMultipleChoiceField(ModelChoiceField)
        ...                        django.forms.models.ModelMultipleChoiceField
     
     
         
    TypedChoiceField(ChoiceField)
        coerce = lambda val: val   对选中的值进行一次转换
        empty_value= ''            空值的默认值
     
    MultipleChoiceField(ChoiceField)
        ...
     
    TypedMultipleChoiceField(MultipleChoiceField)
        coerce = lambda val: val   对选中的每一个值进行一次转换
        empty_value= ''            空值的默认值
     
    ComboField(Field)
        fields=()                  使用多个验证,如下:即验证最大长度20,又验证邮箱格式
                                   fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
     
    MultiValueField(Field)
        PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用
     
    SplitDateTimeField(MultiValueField)
        input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
        input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
     
    FilePathField(ChoiceField)     文件选项,目录下文件显示在页面中
        path,                      文件夹路径
        match=None,                正则匹配
        recursive=False,           递归下面的文件夹
        allow_files=True,          允许文件
        allow_folders=False,       允许文件夹
        required=True,
        widget=None,
        label=None,
        initial=None,
        help_text=''
     
    GenericIPAddressField
        protocol='both',           both,ipv4,ipv6支持的IP格式
        unpack_ipv4=False          解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用
     
    SlugField(CharField)           数字,字母,下划线,减号(连字符)
        ...
     
    UUIDField(CharField)           uuid类型
    

钩子函数

​ 提供自定义的校验方式

​ 钩子函数是在字段校验完毕之后进行的最后一层校验,钩子的方法名是固定的

  • 局部钩子

    ​ 校验单个字段

    class MyForm(forms.Form):
        '''第一层校验'''
        name = forms.CharField(max_length=8, min_length=3, label='用户名')
        pwd = forms.IntegerField(label='密码')
        confirm_pwd = forms.IntegerField(label='确认密码')
        age = forms.IntegerField()
        email = forms.EmailField()
        '''第二层校验'''
        # 1.校验用户名是否已存在
        def clean_name(self):
            name = self.cleaned_data.get('name')
            res = models.User.objects.filter(name=name).first()
            if res:
                return self.add_error('name','用户名已存在')
            return name
        # 2.校验两次密码是否一致
    
    def func(request):
        form_obj = MyForm()
        print(form_obj.fields)
        if request.method == 'POST':
            form_obj = MyForm(request.POST)
            if form_obj.is_valid():
                res = form_obj.cleaned_data
                res.pop('confirm_pwd')
                print(res)
                models.User.objects.create(**res)
        return render(request,'func.html',locals())
    
  • 全局钩子

    ​ 校验多个字段

    def clean(self):
        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
    

modelform组件

modelform是form的优化版本 使用更简单 功能更强大

model from操作

class MyModelForm(forms.ModelForm):
    class Meta:
        model = models.User
        fields = '__all__'
        """此处可以对表单信息进行操作"""
         widgets = {
            "password": forms.widgets.PasswordInput(attrs={"class": "c1"}),
        }
        
  钩子函数:      
    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

class Meta下常用参数:

model = models.Book  # 对应的Model中的类
fields = "__all__"  # 字段,如果是__all__,就是表示列出所有的字段
exclude = None  # 排除的字段
labels = None  # 提示信息
help_texts = None  # 帮助提示信息
widgets = None  # 自定义插件
error_messages = None  # 自定义错误信息

ModelForm的验证

1.与form组件类似,相比于form组件,减少了一步步验证参数的步骤,而是交付于django自动校验

ModelForm表单的验证在调用is_valid() 或访问errors 属性时隐式调用

2.可以通过自定义局部钩子函数和全局钩子函数方法来进行校验

3.若不重写具体字段并设置 validators属性,则modelfrom会按照模型中的字段的validators校验

save()方法

model from的save()方法,根据表单绑定的数据创建并保存数据库对象

还能实现更改数据库中数据,不过在此之前需要添加一个参数为instance

from myapp.models import Book
from myapp.forms import BookForm

# 根据POST数据创建一个新的form对象
>>> form_obj = BookForm(request.POST)

# 创建书籍对象
>>> new_ book = form_obj.save()

# 基于一个书籍对象创建form对象
>>> edit_obj = Book.objects.get(id=1)
# 使用POST提交的数据更新书籍对象
>>> form_obj = BookForm(request.POST, instance=edit_obj)
>>> form_obj.save()
posted @   Nirvana*  阅读(402)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示