ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Django分页器与forms组件

2022-09-08 21:01:45  阅读:263  来源: 互联网

标签:current obj name form self Django forms 组件 page



一、数量批量操作

# 现在我们需要往数据库中插入100000条数据
class index(request):
    for i in range(100000):
         models.Book.objects.create(title=f'第{i}本书')
'''但是这样只直接插入的话数据库会很慢还有可能会直接蹦掉
我们可以优化'''

class 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}本书')]      
'''
    models.Book.objects.bulk_create(book_list)  # 批量添加到数据库中
    book_query = models.Book.objects.all()  # 将数据提取出来展示到页面上
    return render(request,'bookList.html',locals())
# 涉及到大批量数据的创建 直接使用create可能会造成数据库崩溃
    批量数据创建>>>: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)
    models.Book.objects.bulk_create(book_list)  # 批量创建数据
    book_data = models.Book.objects.all()
    # 计算总共的数据条数
    all_count = book_data.count()
    # 2.自定义每页展示的数据条数
    per_page_num = 10
    all_page_num, more = divmod(all_count, per_page_num)  # 计算总页数
    if more:
        all_page_num += 1
    # 1.获取前端想要展示的页码
    current_page = request.GET.get('page', 1)  # 获取用户指定的page 如果没有则默认展示第一页
    try:
        current_page = int(current_page)  # 将用户输入的数字转为整数
    except ValueError:
        current_page = 1
    # 后端提前生成页码标签  因为前端for循环没有range
    html_page = ''
    xxx = current_page
    if current_page < 6:  # 限制用户不能点击负数
        xxx = 6
    for i in range(xxx - 5, xxx + 6):
        if current_page == i:
            html_page += '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i)
        else:
            html_page += '<li><a href="?page=%s">%s</a></li>' % (i, i)
    # 3.定义出切片起始位置
    start_num = (current_page - 1) * per_page_num
    # 4.定义出切片终止位置
    end_num = current_page * per_page_num
    book_query = book_data[start_num:end_num]  # QuerySet [数据对象 数据对象]
    return render(request, 'bookList.html', locals())

# 前端:
    <nav aria-label="Page navigation" class="text-center">
        <ul class="pagination">
            <li>
              <a href="#" aria-label="Previous">
                <span aria-hidden="true">&laquo;</span>
              </a>
            </li>
            {{ html_page|safe }}
            <li>
              <a href="#" aria-label="Next">
                <span aria-hidden="true">&raquo;</span>
              </a>
            </li>
          </ul>
    </nav>

"""
per_page_num = 10
current_page            start_num               end_num
    1                       0                       10
    2                       10                      20
    3                       20                      30

per_page_num = 5
current_page            start_num               end_num
    1                       0                       5
    2                       5                       10  
    3                       10                      15
得出规律:
start_num = (current_page - 1) * per_page_num
end_num = current_page * per_page_num

总数据             每页展示            总页码
100                 10                  10
99                  10                  10
101                 10                  11
"""
# django自带一个分页器组件 但是不太好用 我们自己也写了一个

三、自定义分页器

首先在应用下创建一个空文件夹plugins 然后创建一个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)

1.使用

# 后端:
    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 }}

四、forms前戏

# 我们可以编写一个用户登入功能并且校验数据返回提示信息(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>

# 但是如果字段名有很多的话这样会有点麻烦

五、forms组件

'''form组件
    1.数据校验
        支持提前设置各种校验规则 之后自动校验
     2.渲染页面
        支持直接渲染获取用户数据的各种标签
     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()  # 邮箱必须符合邮箱格式(至少有个@符号)

# 测试环境:
1.数据校验功能
from app01 import views
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.校验如果多传了一些字段 则不参与校验 全程忽略
     """

1.使用form组件

def func(request):
    form_obj = MyForm()  # 产生一个form对象
    if request.method == 'POST':  
        form_obj = MyForm(request.POST)  # 将用户输入的数据实例化到forms对象
        if form_obj.is_valid():  # 判断用户输入是否正确
            print(form_obj.cleaned_data)  # 查看正确的数据
    return render(request,'func.html',locals())  

# 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 %}
"""类中以外的所有标签都不会自动渲染 需要自己编写"""

# 3.展示提示信息
<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="提交">
</form>

补充:

  • forms类中所有的字段数据默认都是必填的,可以添加required=False字段就不必填写
  • forms类中额外传入的字段数据不会做任何的校验
  • forms组件只负责渲染获取用户数据的标签, form表单标签和提交按钮需要自己写
  • 宣染标签中文提示可以使用参数 label指定,不指定默认英文提醒
  • forms 类中填写的校验性参数前端浏览器会识别并添加校验操作,但是前端的校验是可有可无的后端必须要再次校验
  • form表单可以取消浏览器自动添加校验功能的操作novalidate
  • 错误提示信息可以使用参数error_messages修改 不使用默认是英文错误提示

2.form组件重要参数

 

 

 更多知识点:https://www.cnblogs.com/Dominic-Ji/p/9240365.html

六、钩子函数

1.局部钩子

# 前面的字段都只是第一层检验 都没有链接到数据库校验 那么现在我们在注册功能的时候想要判断用户名是否注册过怎么写?

# 可以使用钩子函数  钩子函数也是在form类中编写

from django import forms
class MyForm(forms.Form):
        # 二层校验
        def clean_name(self):  # 名字是固定的 想要校验那个字段就这样写 clean_xxx
            name = self.cleaned_data.get('name')  # 因为只有结果之前字段的校验才能走到这个函数判断所以肯定有值
            res = models.User.objects.filter(name=name).first()  # 将拿出来的姓名与数据库中判断
            if res:
                return self.add_error('name','用户名已存在')
        return name  # 钩子函数要求钩出来的数据还要返回回去

2.全局钩子

# 现在我们想要用户输入两次密码并校验两次密码是否一致

from django import forms
class MyForm(forms.Form):
    # 二次校验
    def clean(self):
        pwd = self.cleaned_data.get('pwd')  # 使用cleaned_data取值 因为前面的字段已经校验成功了才会来到二次校验 所以肯定有值
        confirm_pwd = self.cleaned_data.get('confirm_pwd')
        if not pwd == confirm_pwd:
            return self.add_error('confirm_pwd','两次密码不一致')
        return self.cleaned_data  # 不过局部钩子还是全局钩子都需要把取出来的值 返回回去

七、modelform组件

'''我们在编写form组件的时候需要按照模型层中的字段一样编写一样的字段 这样会很浪费效率
这个时候modelform组件就开发出来了 但是其实也是按照form组件开发的'''

# 使用更简单 功能更强大

# 编写modelform组件
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

1.class Meta下常用字段

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

八、form源码分析

# 其实字段的校验功能没什么好看的 我们可以看判断用户的输入正确
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())

# 我们可以点击is_valid开始查看
    def is_valid(self):
        return self.is_bound and not self.errors

# 我们可以看到返回的是一个self.is_bound and not self.errors
# 两者必须是True才能返回我们先看self.is_bound

点击self.is_bound可以发现只要我们传参那么就肯定是True 那么我们查看self.errors

    @property
    def errors(self):
        if self._errors is None:
            self.full_clean()
        return self._errors  
# 可以看到errors伪装成了属性 然后我们点self.errors它默认就是None 所以肯定会走if分支 那么我们就可以看full_clean

# 点击full_clean我们可以知道 它调用了三个方法
       self.cleaned_data = {}   # 并创建了一个空字典
        self._clean_fields()  # 局部钩子
        self._clean_form()  # 全局钩子
        self._post_clean()  # 内部为pass
# 这三个方法就是form是组件最主要的核心功能了

1.self._clean_fields()

# 点击self._clean_fields()
# 我们从变量名就可以知道这是不能被我们自己调用的 因为被作者隐藏起来了 只能他自己内部调用

for name, bf in self._bound_items():
    field = bf.field  field # 拿到了所有的字段对象
    value = bf.initial if field.disabled else bf.data 
 # 就是如果字段禁用使用默认值因为我们的字段没有禁用所以点击data查看 
返回的是一个 self.form._widget_data_value(self.field.widget, self.html_name)
# 其实就是拿该字段 用户输入的值

继续往下看

for name, bf in self._bound_items():  # name就是字段名称
    field = bf.field  field # 是拿到了所有的字段对象
    value = bf.initial if field.disabled else bf.data  #
    try:
        if isinstance(field, FileField):  # 就是判断字段是不是文件字段
            value = field.clean(value, bf.initial)  # 点击bf.initial其实就是那用另一种取值并校验
        else:
            value = field.clean(value)  # 拿用户输入的值判断
        self.cleaned_data[name] = value  # 往上面的空字典添加键值对 name就是我们写的字段名
        if hasattr(self, "clean_%s" % name):  # 判断我们有没有写局部钩子  (self, clean_字段名) 
            value = getattr(self, "clean_%s" % name)()  # 然后获取局部钩子加括号调用并赋值给value
            self.cleaned_data[name] = value  # 然后将局部钩子执行的值在添加到字典中 所以我们在编写钩子函数的时候需要返回
    except ValidationError as e:
        self.add_error(name, e)  # 将报错信息添加字段后面

2.self._clean_form()

    def _clean_form(self):
        try:
            cleaned_data = self.clean()  # clean() 就是全局钩子 加括号执行并赋值
        except ValidationError as e:
            self.add_error(None, e)
        else: 
            if cleaned_data is not None:  
                self.cleaned_data = cleaned_data

# 其实就是为了执行全局钩子

3.self._post_clean()

# 里面就只有pass  就是留了一个接口

 

标签:current,obj,name,form,self,Django,forms,组件,page
来源: https://www.cnblogs.com/stephenwzh/p/16670836.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有