【Django】第08回 批量操作数据、自定义分页器、from组件

1. 虚拟环境

虚拟环境 virtual environment
它是一个虚拟化,从电脑独立开辟出来的环境。通俗的来讲,虚拟环境就是借助虚拟机docker来把一部分内容独立出来,我们把这部分独立出来的东西称作“容器”,在这个容器中,我们可以只安装我们需要的依赖包,各个容器之间互相隔离,互不影响。譬如,本次学习需要用到Django,我们可以做一个Django的虚拟环境,里面只需要安装Django相关包就可以了,需要Scrapy库,就在开辟一个独立空间来学习Scrapy库相关就行了。

2. 批量操作数据

浏览器访问一个django路由 立刻创建10万条数据并展示到前端页面
    create()、all()

涉及到大批量数据的创建 直接使用create可能会造成数据库崩溃
    批量数据创建>>>:bulk_create() 
    批量数据修改>>>:bulk_update()
def index(request):
    # for i in range(1000):
    #     models.Book.objects.create(title=f'第{i}本书')
    book_list = []
    for i in range(1000):
        book_obj = models.Book(title='第{i}本书')
        book_list.append(book_obj)
    '''以上四行可以简写为一行>>>:列表生成式'''
    # [models.Book(title='第{i}本书') for i in range(1000)]
    models.Book.objects.bulk_create(book_list)  # 批量创建数据
    # models.Book.objects.bulk_update()  # 批量修改
    book_query = models.Book.objects.all()
    return render(request, 'bookList.html', locals())

3. 批量数据展示

当数据量比较大的时候 页面展示应该考虑分页
分页器组件的推导流程
1.QuerySet切片操作
  1.获取前端想要展示的页码
  2.自定义每页展示的数据条数
  3.定义出切片起始位置
  4.定义出切片终止位置
2.分页样式添加
3.页码展示
  如何根据总数据和每页展示的数据得出总页码
  divmod()
4.如何渲染出所有的页码标签
  前端模板语法不支持range 但是后端支持 我们可以在后端创建好 
  html标签然后传递给html页面使用
5.如何限制住展示的页面标签个数
  页码推荐使用奇数位(对称美)  
  利用当前页前后固定位数来限制
6.首尾页码展示范围问题

4. 自定义分页器

创建plugins包存第三方后端模块,创建mypage.py文件加入代码
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:  最多显示的页码个数
        """
        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)

<div class="col-md-8 col-md-offset-2">
             {% for book_obj in page_query %}
                <p class="text-center">{{ book_obj.title }}</p>
             {% endfor %}
             {{ page_obj.page_html|safe }}
</div>
def index(request):
    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())

5. Form组件

5.1 前戏

编写用户登录功能并且校验数据返回提示信息(form表单)
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 action="" method="post">
    <p>username:
        <input type="text" name="username">
        <span style="color: red">{{ data_dict.username }}</span>
    </p>
    <p>password:
        <input type="text" name="password">
        <span style="color: red">{{ data_dict.password }}</span>

    </p>
    <input type="submit">
</form>

5.2 Form组件功能

1.数据校验
      支持提前设置各种校验规则 之后自动校验
2.渲染页面
      支持直接渲染获取用户数据的各种标签
3.展示信息
      支持针对不同的校验失败展示不同的提示

5.3 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()  # 邮箱必须符合邮箱格式(至少有个@符号)

5.3 数据校验功能

单独对某个py文件测试  console
1.导入模块
  from app01 import views
2.传递待校验的数据
  form_obj = views.MyForm({'name':'jason','age':18,'email':123})
3.判断所有的数据是否符合校验
  form_obj.is_valid()
4.获取符合校验规则的数据
  form_obj.cleaned_data
  {'name': 'jason', 'age': 18}
5.查阅不符合校验规则的数据及错误原因
  form_obj.errors
  {'email': ['Enter a valid email address.']}



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

5.4 渲染标签功能

注释区别

{# 看不见我#}
<!--看得见我-->

渲染方式1

封装程度高 扩展性差
{{ form_obj.as_p }}
{{ form_obj.as_table }}
{{ form_obj.as_ul }}

渲染方式2

封装程度低 扩展性好 编写困难
{{ form_obj.name.lable }}  
{{ form_obj.name }}
{{ form_obj.name.lable }}  
{{ form_obj.name }}
{{ form_obj.name.lable }}  
{{ form_obj.name }}

渲染方式3

推荐使用
{% for form in form_obj %}
<p>{{ form.label }}{{ form }}</p>
{% endfor %}

5.5 展示提示信息

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

5.6 重要的字段参数

is_valid()		判断是否符合校验规则  返回布尔值
error()			查阅不符合规则及错误原因
cleaned_datah	        获取符合规则的数据
label			字段名
max_length		最大长度
min_length		最小长度
max_value		最大值
min_value		最小值    
error_messages		错误提示
required	        是否为空
widget		        标签类型、标签属性
initial		        默认值
validators		正则校验

6. 钩子函数

提供自定义的校验方式:
    局部钩子:校验单个字段
    全局钩子:校验多个字段

6.1 局部钩子

局部钩子:校验单个字段

class MyForm(forms.Form):
    '''属于第一层校验'''
    # 用户名最长8个字符,最短3个字符
    name = forms.CharField(max_length=8, min_length=3, label='用户名')
    pwd = forms.IntegerField(label='密码')
    confirm_pwd = forms.IntegerField(label='确认密码')
    # 用户年龄最大150,最小0岁
    age = forms.IntegerField()
    # 邮箱必须符合邮箱格式
    email = forms.EmailField()
    '''属于第二层校验,第一层校验通过以后才会执行这层校验'''
    # 1.校验用户名是否已经存在
    def clean_name(self):
        # 勾子函数会等上面的校验通过以后才会执行,相当于最后一层校验
        # 第二层校验的话cleaned_data里面肯定会有值,因为第一层校验已经通过了
        name = self.cleaned_data.get('name')
        res = models.User.objects.filter(name=name).first()
        if res:
            # 给前端一个消息,这个消息鬼属于name框,出错了提示会放在name框中
            return self.add_error('name', '用户名已存在')
        # 没有问题的话,记得拿了什么东西要返回回去,“把人家的东西勾来了,记得给放回去”
        return name

6.2 全局钩子

全局钩子:校验多个字段

# 2.校验俩次密码是否一致
# clean这个字段就是校验多个字段的,固定的
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

7. modelform组件

modelform是form的优化版本 使用更简单 功能更强大
Django 提供一个辅助类来让我们可以从Django 的模型创建Form,这就是ModelForm。

form与model的终极结合

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

class Meta下常用参数

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

ModelForm的验证

与普通的Form表单验证类型类似,ModelForm表单的验证在调用is_valid() 或访问errors 属性时隐式调用。
我们可以像使用Form类一样自定义局部钩子方法和全局钩子方法来实现自定义的校验规则。
如果我们不重写具体字段并设置validators属性的化,ModelForm是按照模型中字段的validators来校验的。

save()方法
每个ModelForm还具有一个save()方法。 这个方法根据表单绑定的数据创建并保存数据库对象。 ModelForm的子类可以接受现有的模型实例作为关键字参数instance;如果提供此功能,则save()将更新该实例。 如果没有提供,save() 将创建模型的一个新实例:
posted @   |相得益张|  阅读(53)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示