分页器与modelform

分页器

批量操作数据

1.浏览器访问一个django路由,创建100条数据并展示到前端页面

2.当使用create创建大量数据时可能会造成数据库崩溃
批量数据创建>>>:bulk_create() 
批量数据修改>>>:bulk_update()

3.实例
def index(request):
    book_list = []
    for i in range(100):
        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())

4.html
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            {% for obj in book_query %}
                <p class="text-center">{{ obj.title }}</p>
            {% endfor %}
        </div>
    </div>
</div>

批量数据展示推导

1.当展示的数据量较大时,页面展示应该考虑分页
2.使用QuerySet切片操作
3.复制一个分页样式添加到html文件中

4.根据总数据和每页页数展示数据得出的总页码
all_page_num, more = divmod(all_count, per_page_num)

5.前端模板不支持range,但是后端支持,所以我们可以在后端创建好html标签然后传递给前端html页面使用,这样就可以渲染出所有的页码标签
html_page += '<li><a href="?page=%s">%s</a></li>' % (i, i)

6.页码推荐使用奇数位(对称美),利用当前页前后固定位数来限制展示的页码标签个数,在用判断限制首尾页码展示范围

7.实例
def index(request):
    # 获取所有数据
    book_data = models.Book.objects.all()
    # 计算数据条数
    all_count = book_data.count()
    # 定义每页展示的数据条数
    per_page_num = 10
    # 获取每页10条数据需要的页数
    all_page_num, more = divmod(all_count, per_page_num)
    if more:
        all_page_num += 1
    # 获取用户指定的page,如果没有则默认展示第一页
    current_page = request.GET.get('page', 1)
    # 用户输入字符串则默认展示第一页
    try:
        current_page = int(current_page)
    except ValueError:
        current_page = 1
    # 后端提前生成页码标签
    html_page = ''
    xxx = current_page
    # 如果用户访问的页面小于6,则只渲染到6
    if current_page < 6:
        xxx = 6
    # 如果用户访问的页面大于最大页数,则只渲染到最大页数-5
    if current_page > (all_page_num - 5):
        xxx = (all_page_num - 5)
    # 以用户所选择的页面为中间也,左右各展示五个
    for i in range(xxx - 5, xxx + 6):
        if current_page == i:
            # 如果当前渲染的标签页与用户服务的页面一致,就加class="active"
            html_page += '<li><a class="active"href="?page=%s">%s</a></li>' % (i, i)
        else:
            # 如果当前渲染的标签页与用户服务的页面不一致,就不加
            html_page += '<li><a href="?page=%s">%s</a></li>' % (i, i)
    # 定义切片起始位置
    start_num = (current_page - 1) * per_page_num
    # 定义切片终止位置
    end_num = current_page * per_page_num
    # 分页处理
    book_query = book_data[start_num:end_num]
    return render(request, 'booklist.html', locals())

'上述只是分页组件的推导流程,实际中无需编写'

自定义分页器

1.我们可以直接导入写好的分页器
from APP01.utils 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())

2.分页器
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)

modelform

form组件

1.用form表单编写用户登录功能并且校验数据返回提示信息
def form(request):
    data_dict = {'username': '', 'password': ''}
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        if username == 'barry':
            data_dict['username'] = 'barry不可以哦'
        if password == '123':
            data_dict['password'] = '太短啦'
    return render(request, 'abform.html', locals())

2.html
<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>

form组件

1.数据校验
支持提前设置各种校验规则,之后自动校验

2.渲染页面
支持直接渲染获取用户数据的各种标签

3.展示信息
支持针对不同的校验失败展示不同的提示

4.form表单创建
class User(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    email = models.EmailField()

5.form类型创建
from django import forms
class MyForm(forms.Form):
    # 用户名最长八个字符,最短三个字符
    name = forms.CharField(max_length=8, min_length=3)
    # 年龄最小0岁,最大120岁
    age = forms.IntegerField(max_value=120, min_value=0)
    # 邮箱必须符合格式,至少有个@符
    email = forms.EmailField()

form组件校验数据功能

1.传递待校验的数据
form_obj = views.MyForm({'name':'barry','age':18,'email':123})

2.判断所有的数据是否符合校验
form_obj.is_valid()

3.获取符合校验规则的数据
orm_obj.cleaned_data  # {'name': 'barry', 'age': 18}

4.查阅不符合校验规则的数据及错误原因
form_obj.errors  # {'email': ['Enter a valid email address.']}
'''
from类中编写的字段默认是必填的,少传了字段就无法通过校验
校验传多了不存在字段的数据,不会参与其他功能
'''

form组件渲染标签功能

1.后端要获取我们前面写的MyForm类的对象
def func(request):
    form_obj = MyForm()
    return render(request, 'abform.html', locals())

2.方式1(封装程度高,扩展性差)
{{ form_obj.as_p }}  # 将字段一行行展示p标签展示
{{ form_obj.as_table }}  # 将字段label、input标签展示
{{ form_obj.as_ul }}  # 将字段渲染成li标签展示

3.方式2(封装程度低 扩展性好,编写困难)
{{ form_obj.name.lable }}  # 展示字段名字
{{ form_obj.name }}  # 展示字段名的input框

4.方式3(推荐使用)
{% for form in form_obj %}
<p>{{ form.label }}{{ form }}</p>
{% endfor %}
'渲染的标签都是类中存在的,类中以外的所有标签都不会自动渲染,需要自己编写'

form组件展示提示信息

1.在后端进行数据校验
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)
        else:
            # 返回错误信息
            print(form_obj.errors)
    return render(request, 'abform.html', locals())

2.html
<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="提交">

.修改django默认语言
# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'zh-hans'
from django.conf import global_settings  # 字符地址

form组件重要的字段参数

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

钩子函数

1.局部钩子
校验单个字段
def clean_name(self):
    # 获取用户名
    name = self.cleaned_data.get('name')
    # 用用户名从数据库获取用户数据
    res = models.User.objects.filter(name=name)
    if res:
        # 若用户名已存在
        return self.add_error('name', '用户名已存在')
    # 若是该用户名不存在
    return name

2.全局钩子
效验多个字段
def clean(self):
    # 获取想要的字段数据
    pwd = self.cleaned_data.get('pwd')
    confirm_pwd = self.cleaned_data.get('confirm_pwd')
    if pwd == confirm_pwd:
        # 若用户输入的两次密码不一致
        return self.add_error('confirm_pwd', '两次密码不一致')
    # 若用户输入的密码一致则返回数据
    return self.cleaned_data

modelform组件

1.ModelForm是将Model和Form进行绑定,Form可以自动生成表单的作用,但是每一个forms字段需要自己手动填写,而Model就是数据库包含了所有的数据字段,所以ModelForm有着Form的所有功能也能将Model字段自动转成forms字段

2.实例
from django import forms
class MyModeForm(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:
            # 若用户名已存在
            return self.add_error('name', '用户名已存在')
        # 若是该用户名不存在则数据返回
        return name

3.常用参数
model = models.Book>>>与那个类做对应
fields = '__all__'>>>表示列出所有的字段
exclude = None>>>排除调不参与的字段
labels = None>>>给标签加提示信息
help_texts = None>>>帮助提示信息
widgets = None>>>给标签加属性
error_messages = None>>>自己定义错误信息
posted @ 2022-09-08 22:59  无言以对啊  阅读(50)  评论(0)    收藏  举报