目录
一、分页
1、修改case_list.html页面
2、修改views.py的case_list方法(分页未封装)
二、分页封装
1、新建类Pagination
2、修改views.py的case_list方法
三、再优化,实现搜索+分页qing情况
四、优化其他查询页面实现分页和查询
五、优化分页显示总页数和总条数
接上一篇《django项目实战二(django+bootstrap实现增删改查)进阶查询》
知识点:
分页的封装
一、分页

1、修改case_list.html页面

{% extends 'layout.html' %}
{% block title %}
    <title>用例列表</title>
{% endblock %}
{% block content %}
    <div class="container">
        <div style="margin-bottom: 10px" class="clearfix">
            <a class="btn btn-success" href="/case/add/">
                <span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>
                新建
            </a>
            <div style="float: right;width: 300px">
                <form method="get">
                    <div class="input-group">
                        <input type="text" name="q" class="form-control" placeholder="请输入用例名称"
                               value="{{ search_data }}">
                        <span class="input-group-btn">
                            <button class="btn btn-default" type="submit">
                                <span class="glyphicon glyphicon-search" aria-hidden="true"></span>
                            </button>
                        </span>
                    </div>
                </form>
            </div>
        </div>
        <div class="bs-example" data-example-id="panel-without-body-with-table">
            <div class="panel panel-default">
                <!-- Default panel contents -->
                <div class="panel-heading">
                    <span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>
                    用例列表
                </div>
                <!-- Table -->
                <table class="table table-bordered">
                    <thead>
                    <tr>
                        <th>序号</th>
                        <th>用例编号</th>
                        <th>用例名称</th>
                        <th>步骤</th>
                        <th>期望结果</th>
                        <th>实际结果</th>
                        <th>优先级</th>
                        <th>作者</th>
                        <th>状态</th>
                        <th>缺陷编号</th>
                        <th>操作</th>
                    </tr>
                    </thead>
                    <tbody>
                    {% for obj in case_set %}
                        <tr>
                            <th scope="row">{{ obj.id }}</th>
                            <td>{{ obj.number }}</td>
                            <td>{{ obj.name }}</td>
                            <td>{{ obj.step }}</td>
                            <td>{{ obj.expect }}</td>
                            <td>{{ obj.actual|default_if_none:" " }}</td>
                            <td>{{ obj.get_priority_display }}</td>
                            <td>{{ obj.author }}</td>
                            <td>{{ obj.get_status_display }}</td>
                            <td>{{ obj.bug_no|default_if_none:" " }}</td>
                            <td>
                                <a class="btn btn-primary btn-xs" href="/case/{{ obj.id }}/edit/">编辑</a>
                                <a class="btn btn-danger btn-xs" href="/case/{{ obj.id }}/delete/">删除</a>
                            </td>
                        </tr>
                    {% endfor %}
                    </tbody>
                </table>
            </div>
        </div>
        <ul class="pagination">
            {{ page_string }}          
        </ul>
    </div>
{% endblock %}2、修改views.py的case_list方法(分页未封装)
from django.core.exceptions import ValidationError
from django.shortcuts import render, redirect, HttpResponse
from TestManagementSystem import models
from django import forms
from django.core.validators import RegexValidator
from django.utils.safestring import mark_safe
# Create your views here.
def depart_list(request):
    """部门列表"""
    # 查询所有部门
    print("部门列表")
    depart_set = models.Department.objects.all()
    return render(request, 'depart_list.html', {"depart_set": depart_set})
def depart_add(request):
    """新增部门"""
    # 新增部门
    # return HttpResponse("成功")
    if request.method =='GET':
        return render(request, 'depart_add.html')
    depart_name = request.POST.get("departname")
    models.Department.objects.create(name=depart_name)
    return redirect('/depart/list')
def depart_delete(request):
    """删除部门"""
    depart_id = request.GET.get("departid")
    models.Department.objects.filter(id=depart_id).delete()
    return redirect('/depart/list')
def depart_edit(request, nid):
    """编辑部门"""
    if request.method == 'GET':
        row_object = models.Department.objects.filter(id=nid).first()
         # print(row_object.id, row_object.name)
        return render(request, 'depart_edit.html', {"row_object": row_object})
    # 获取用户提交的部门名称
    edit_depart_name = request.POST.get("departname")
    # 根据编辑页面用户ID去更新部门的名称
    models.Department.objects.filter(id=nid).update(name=edit_depart_name)
    return redirect('/depart/list')
def user_list(request):
    """用户列表"""
    # 查询所有用户
    user_set = models.UserInfo.objects.all()
    """
    for obj in user_set:
        print(obj.id, obj.name, obj.password, obj.account, obj.create_time.strftime("%Y-%m-%d-%H-%M-%S"),
              obj.get_gender_display(), obj.depart.name)
    """
    return render(request, 'user_list.html', {"user_set": user_set})
def user_add(request):
    """新增用户(原始方式)"""
    if request.method == 'GET':
        # 这个是为了新增页面动态获取性别
        context = {
            "gender_choices": models.UserInfo.gender_choices,
            "depart_list": models.Department.objects.all()
        }
        return render(request, 'user_add.html', context)
    user_name = request.POST.get("username")
    password = request.POST.get("pwd")
    age = request.POST.get("age")
    account = request.POST.get("ac")
    create_time = request.POST.get("ctime")
    gender = request.POST.get("gd")
    depart_id = request.POST.get("dp")
    models.UserInfo.objects.create(name=user_name, password=password,
                                   age=age, account=account,
                                   create_time=create_time,
                                   gender=gender, depart_id=depart_id)
    return redirect('/user/list')
class UserModelForm(forms.ModelForm):
    # 限制姓名的长度,至少为3位
    name = forms.CharField(min_length=3, label='用户名')
    # password = forms.CharField(label='密码',validators='这里写正则表达式')
    class Meta:
        model = models.UserInfo
        fields = ["name", "password", "age", "account", "create_time", "gender", "depart"]
        '''widgets = {
            "name": forms.TextInput(attrs={"class": "form-control"}),
            "password": forms.PasswordInput(attrs={"class": "form-control"}),
            "age": forms.TextInput(attrs={"class": "form-control"}),
            "account": forms.TextInput(attrs={"class": "form-control"})           
        }'''  # 下方方法更好
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 循环找到所有插件,添加了class: "from-control"
        for name, field in self.fields.items():
            field.widget.attrs = {"class": "form-control", "placeholder": field.label}
def user_model_form_add(request):
    """新增用户(ModelForm方式)"""
    if request.method == 'GET':
        form = UserModelForm()
        return render(request, 'user_model_form_add.html', {"form": form})
    # POST 请求提交的数据,数据校验
    form = UserModelForm(data=request.POST)
    if form.is_valid():
        # 如果数据合法,这里判断的是所有字段不能为空,则存储到数据库
        # models.UserInfo.objects.create(..) 常规存储方式
        form.save()
        return redirect('/user/list')
    # 如果不满足if判断进入到else返回错误信息
    return render(request, 'user_model_form_add.html', {"form": form})
def user_edit(request, nid):
    """编辑用户"""
    # 根据nid去数据库获取所在行数据
    row_object = models.UserInfo.objects.filter(id=nid).first()
    if request.method == 'GET':
        form = UserModelForm(instance=row_object)
        return  render(request, 'user_edit.html', {"form": form})
    # POST 请求提交的数据,数据校验
    form = UserModelForm(data=request.POST, instance=row_object)
    if form.is_valid():
        # 如果数据合法,这里判断的是所有字段不能为空,则存储到数据库
        # models.UserInfo.objects.create(..) 常规存储方式
        # form.instance.字段名=值  # 如果需要存储用户输入之外的值使用这个
        form.save()
        return redirect('/user/list')
        # 如果不满足if判断进入到else返回错误信息
    return render(request, 'user_edit.html', {"form": form})
def user_delete(request, nid):
    """删除用户"""
    # 根据nid去数据库获取所在行数据进行删除
    models.UserInfo.objects.filter(id=nid).delete()
    return redirect('/user/list')
def case_list(request):
    """用例列表"""
    # 查询所有用例
    data_dict = {}
    # 获取浏览器传过来的值
    search_data = request.GET.get('q', "")
    if search_data:
        data_dict["name__contains"] = search_data
    # 根据用户想要的访问页码,计算出起止位置
    page = int(request.GET.get('page', 1))
    page_size = 10  # 每页显示数据量
    start = (page - 1) * page_size
    end = page * page_size
    case_set = models.Case.objects.filter(**data_dict).order_by('-id')[start:end]
    # 数据总条数
    total_count = models.Case.objects.filter(**data_dict).order_by('-id').count()
    total_page_count, div = divmod(total_count, page_size)
    if div:
        total_page_count += 1
    # 计算出,线上当前页的前5页,后5页,plus=5
    plus = 5
    # 数据未到达11页(数据量不达标时)
    if total_count <= 2 * plus + 1:
        start_page = 1
        end_page = total_page_count
    else:
        # 当前页< 5(前面的异常考虑)
        if page <= plus:
            start_page = 1
            end_page = 2 * plus + 1
        else:
            # 当前页+plus大于总页面(后面的异常考虑)
            if page + plus  > total_page_count:
                start_page = total_page_count - 2 * plus
                end_page = total_page_count
            else:
                # 前五页,后五页
                start_page = page - plus
                end_page = page + plus
    # 页码
    page_str_list = []
    # 首页
    page_str_list.append('<li><a href="?page={}">首页</a></li>'.format(1))
    # 上一页
    if page > 1:
        prev = '<li><a href="?page={}">上一页</a></li>'.format(page-1)
    else:
        prev = '<li><a href="?page={}">上一页</a></li>'.format(1)
    page_str_list.append(prev)
    # 页面
    for i in range(start_page, end_page + 1):
        if i == page:
            ele = '<li class="active"><a href="?page={}">{}</a></li>'.format(i, i)
        else:
            ele = '<li><a href="?page={}">{}</a></li>'.format(i, i)
        page_str_list.append(ele)
    # 下一页
    if page < total_page_count:
        prev = '<li><a href="?page={}">下一页</a></li>'.format(page + 1)
    else:
        prev = '<li><a href="?page={}">下一页</a></li>'.format(total_page_count)
    page_str_list.append(prev)
    # 尾页
    page_str_list.append('<li><a href="?page={}">尾页</a></li>'.format(total_page_count))
    # 跳转
    search_string = """
    <li>
        <form style="float: left;margin-left: -1px" method="get">
            <input name="page"
                   style="position: relative;float: left;display: inline-block;width: 80px;border-radius: 0"
                   type="text"  class="form-control" placeholder="页码">
                <button style="border-radius: 0" class="btn btn-default" type="submit">跳转</button>
            </span>
        </form>
    </li>
    """
    page_str_list.append(search_string)
    # 拼接html
    page_string = mark_safe("".join(page_str_list))
    return render(request, 'case_list.html', {"case_set": case_set, "search_data": search_data, "page_string": page_string})
class CaseModelForm(forms.ModelForm):
    number = forms.CharField(
        label="用例编号",
        validators=[RegexValidator(r'^0\d{3}$', '数字必须以0开头的4位数字')],
    )
    class Meta:
        model = models.Case
        # fields = ["number", "name", "step", "expect", "actual", "priority", "author", "status", "bug_no"]
        fields = "__all__"  # 这个表示所有字段
        # exclude = ["bug_no"] #  排除字段
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 循环找到所有插件,添加了class: "from-control"
        for name, field in self.fields.items():
            field.widget.attrs = {"class": "form-control", "placeholder": field.label}
    # 钩子函数进行判重验证,这个名字注意是clean_加字段名
    def clean_number(self):
        tex_number = self.cleaned_data['number']
        exists = models.Case.objects.filter(number=tex_number).exists()
        if exists:
            raise ValidationError("用例编号已存在")
        return tex_number
def case_add(request):
    """新增用例(ModelForm方式)"""
    if request.method == 'GET':
        form = CaseModelForm()
        return render(request, 'case_add.html', {"form": form})
    # POST 请求提交的数据,数据校验
    form = CaseModelForm(data=request.POST)
    if form.is_valid():
        # 如果数据合法,这里判断的是所有字段不能为空,则存储到数据库
        # models.UserInfo.objects.create(..) 常规存储方式
        form.save()
        return redirect('/case/list')
    # 如果不满足if判断进入到else返回错误信息
    return render(request, 'case_add.html', {"form": form})
class CaseEditModelForm(forms.ModelForm):
    # 控制字段显示,但是不可编辑
    number = forms.CharField(disabled=True, label="用例编号")
    class Meta:
        model = models.Case
        fields = ["number", "name", "step", "expect", "actual", "priority", "author",  "status", "bug_no"]
        # fields = "__all__"  # 这个表示所有字段
        # exclude = ["bug_no"] #  排除字段
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 循环找到所有插件,添加了class: "from-control"
        for name, field in self.fields.items():
            field.widget.attrs = {"class": "form-control", "placeholder": field.label}
    # 钩子函数进行判重验证,这个名字注意是clean_加字段名
    def clean_number(self):
        # 获取当前编辑那一行的ID,从POST那里获取到了instance
        # print(self.instance.pk)
        tex_number = self.cleaned_data['number']
        exists = models.Case.objects.exclude(id=self.instance.pk).filter(number=tex_number).exists()
        if exists:
            raise ValidationError("用例编号已存在")
        return tex_number
def case_edit(request, nid):
    """编辑用户"""
    # 根据nid去数据库获取所在行数据
    row_object = models.Case.objects.filter(id=nid).first()
    if request.method == 'GET':
        form = CaseEditModelForm(instance=row_object)
        return  render(request, 'case_edit.html', {"form": form})
    # POST 请求提交的数据,数据校验
    form = CaseEditModelForm(data=request.POST, instance=row_object)
    if form.is_valid():
        # 如果数据合法,这里判断的是所有字段不能为空,则存储到数据库
        # models.UserInfo.objects.create(..) 常规存储方式
        # form.instance.字段名=值  # 如果需要存储用户输入之外的值使用这个
        form.save()
        return redirect('/case/list')
        # 如果不满足if判断进入到else返回错误信息
    return render(request, 'case_edit.html', {"form": form})
def case_delete(request, nid):
    """删除用例"""
    # 根据nid去数据库获取所在行数据进行删除
    models.Case.objects.filter(id=nid).delete()
    return redirect('/case/list')
二、分页封装
1、新建类Pagination
在应用下新建文件夹utils,然后在下面创建pagination.py模块

# -*- coding: utf-8 -*-
# @Time    : 2023/2/20 15:24
# @Author  : caicloud
# @File    : pagination.py
# @Software: PyCharm
# @Describe: 自定义分页组件
from django.utils.safestring import mark_safe
class Pagination(object):
    def __init__(self, request, queryset, page_size=10, page_param='page', plus=5):
        page = request.GET.get(page_param, "1")
        if page.isdecimal():
            page = int(page)
        else:
            page = 1
        self.page = page
        self.page_size = page_size
        self.start = (self.page - 1) * self.page_size
        self.end = self.page * self.page_size
        self.page_queryset = queryset[self.start: self.end]
        # 数据总条数
        total_count = queryset.count()
        total_page_count, div = divmod(total_count, page_size)
        if div:
            total_page_count += 1
        self.total_page_count = total_page_count
        self.plus = plus
    def html(self):
        # 计算出,线上当前页的前5页,后5页,plus=5
        # 数据未到达11页(数据量不达标时)
        if self.total_page_count <= 2 * self.plus + 1:
            start_page = 1
            end_page = self.total_page_count
        else:
            # 当前页< 5(前面的异常考虑)
            if self.page <= self.plus:
                start_page = 1
                end_page = 2 * self.plus + 1
            else:
                # 当前页+plus大于总页面(后面的异常考虑)
                if self.page + self.plus > self.total_page_count:
                    start_page = self.total_page_count - 2 * self.plus
                    end_page = self.total_page_count
                else:
                    # 前五页,后五页
                    start_page = self.page - self.plus
                    end_page = self.page + self.plus
        # 页码
        page_str_list = []
        # 首页
        page_str_list.append('<li><a href="?page={}">首页</a></li>'.format(1))
        # 上一页
        if self.page > 1:
            prev = '<li><a href="?page={}">上一页</a></li>'.format(self.page - 1)
        else:
            prev = '<li><a href="?page={}">上一页</a></li>'.format(1)
        page_str_list.append(prev)
        # 页面
        for i in range(start_page, end_page + 1):
            if i == self.page:
                ele = '<li class="active"><a href="?page={}">{}</a></li>'.format(i, i)
            else:
                ele = '<li><a href="?page={}">{}</a></li>'.format(i, i)
            page_str_list.append(ele)
        # 下一页
        if self.page < self.total_page_count:
            prev = '<li><a href="?page={}">下一页</a></li>'.format(self.page + 1)
        else:
            prev = '<li><a href="?page={}">下一页</a></li>'.format(self.total_page_count)
        page_str_list.append(prev)
        # 尾页
        page_str_list.append('<li><a href="?page={}">尾页</a></li>'.format(self.total_page_count))
        # 跳转
        search_string = """
            <li>
                <form style="float: left;margin-left: -1px" method="get">
                    <input name="page"
                           style="position: relative;float: left;display: inline-block;width: 80px;border-radius: 0"
                           type="text"  class="form-control" placeholder="页码">
                        <button style="border-radius: 0" class="btn btn-default" type="submit">跳转</button>
                    </span>
                </form>
            </li>
            """
        page_str_list.append(search_string)
        # 拼接html
        page_string = mark_safe("".join(page_str_list))
        return page_string2、修改views.py的case_list方法

from django.core.exceptions import ValidationError
from django.shortcuts import render, redirect, HttpResponse
from TestManagementSystem import models
from django import forms
from django.core.validators import RegexValidator
from TestManagementSystem.utils.pagination import Pagination
# Create your views here.
def depart_list(request):
    """部门列表"""
    # 查询所有部门
    print("部门列表")
    depart_set = models.Department.objects.all()
    return render(request, 'depart_list.html', {"depart_set": depart_set})
def depart_add(request):
    """新增部门"""
    # 新增部门
    # return HttpResponse("成功")
    if request.method =='GET':
        return render(request, 'depart_add.html')
    depart_name = request.POST.get("departname")
    models.Department.objects.create(name=depart_name)
    return redirect('/depart/list')
def depart_delete(request):
    """删除部门"""
    depart_id = request.GET.get("departid")
    models.Department.objects.filter(id=depart_id).delete()
    return redirect('/depart/list')
def depart_edit(request, nid):
    """编辑部门"""
    if request.method == 'GET':
        row_object = models.Department.objects.filter(id=nid).first()
         # print(row_object.id, row_object.name)
        return render(request, 'depart_edit.html', {"row_object": row_object})
    # 获取用户提交的部门名称
    edit_depart_name = request.POST.get("departname")
    # 根据编辑页面用户ID去更新部门的名称
    models.Department.objects.filter(id=nid).update(name=edit_depart_name)
    return redirect('/depart/list')
def user_list(request):
    """用户列表"""
    # 查询所有用户
    user_set = models.UserInfo.objects.all()
    """
    for obj in user_set:
        print(obj.id, obj.name, obj.password, obj.account, obj.create_time.strftime("%Y-%m-%d-%H-%M-%S"),
              obj.get_gender_display(), obj.depart.name)
    """
    return render(request, 'user_list.html', {"user_set": user_set})
def user_add(request):
    """新增用户(原始方式)"""
    if request.method == 'GET':
        # 这个是为了新增页面动态获取性别
        context = {
            "gender_choices": models.UserInfo.gender_choices,
            "depart_list": models.Department.objects.all()
        }
        return render(request, 'user_add.html', context)
    user_name = request.POST.get("username")
    password = request.POST.get("pwd")
    age = request.POST.get("age")
    account = request.POST.get("ac")
    create_time = request.POST.get("ctime")
    gender = request.POST.get("gd")
    depart_id = request.POST.get("dp")
    models.UserInfo.objects.create(name=user_name, password=password,
                                   age=age, account=account,
                                   create_time=create_time,
                                   gender=gender, depart_id=depart_id)
    return redirect('/user/list')
class UserModelForm(forms.ModelForm):
    # 限制姓名的长度,至少为3位
    name = forms.CharField(min_length=3, label='用户名')
    # password = forms.CharField(label='密码',validators='这里写正则表达式')
    class Meta:
        model = models.UserInfo
        fields = ["name", "password", "age", "account", "create_time", "gender", "depart"]
        '''widgets = {
            "name": forms.TextInput(attrs={"class": "form-control"}),
            "password": forms.PasswordInput(attrs={"class": "form-control"}),
            "age": forms.TextInput(attrs={"class": "form-control"}),
            "account": forms.TextInput(attrs={"class": "form-control"})           
        }'''  # 下方方法更好
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 循环找到所有插件,添加了class: "from-control"
        for name, field in self.fields.items():
            field.widget.attrs = {"class": "form-control", "placeholder": field.label}
def user_model_form_add(request):
    """新增用户(ModelForm方式)"""
    if request.method == 'GET':
        form = UserModelForm()
        return render(request, 'user_model_form_add.html', {"form": form})
    # POST 请求提交的数据,数据校验
    form = UserModelForm(data=request.POST)
    if form.is_valid():
        # 如果数据合法,这里判断的是所有字段不能为空,则存储到数据库
        # models.UserInfo.objects.create(..) 常规存储方式
        form.save()
        return redirect('/user/list')
    # 如果不满足if判断进入到else返回错误信息
    return render(request, 'user_model_form_add.html', {"form": form})
def user_edit(request, nid):
    """编辑用户"""
    # 根据nid去数据库获取所在行数据
    row_object = models.UserInfo.objects.filter(id=nid).first()
    if request.method == 'GET':
        form = UserModelForm(instance=row_object)
        return  render(request, 'user_edit.html', {"form": form})
    # POST 请求提交的数据,数据校验
    form = UserModelForm(data=request.POST, instance=row_object)
    if form.is_valid():
        # 如果数据合法,这里判断的是所有字段不能为空,则存储到数据库
        # models.UserInfo.objects.create(..) 常规存储方式
        # form.instance.字段名=值  # 如果需要存储用户输入之外的值使用这个
        form.save()
        return redirect('/user/list')
        # 如果不满足if判断进入到else返回错误信息
    return render(request, 'user_edit.html', {"form": form})
def user_delete(request, nid):
    """删除用户"""
    # 根据nid去数据库获取所在行数据进行删除
    models.UserInfo.objects.filter(id=nid).delete()
    return redirect('/user/list')
def case_list(request):
    """用例列表"""
    # 查询所有用例
    data_dict = {}
    # 获取浏览器传过来的值
    search_data = request.GET.get('q', "")
    if search_data:
        data_dict["name__contains"] = search_data
    case_set = models.Case.objects.filter(**data_dict).order_by('-id')
    # 实例化封装的分页
    page_object = Pagination(request, case_set)
    context = {
        "search_data": search_data,  # 查询
        "case_set": page_object.page_queryset,  # 分完页的数据
        "page_string": page_object.html()  # 页码
    }
    return render(request, 'case_list.html', context)
class CaseModelForm(forms.ModelForm):
    number = forms.CharField(
        label="用例编号",
        validators=[RegexValidator(r'^0\d{3}$', '数字必须以0开头的4位数字')],
    )
    class Meta:
        model = models.Case
        # fields = ["number", "name", "step", "expect", "actual", "priority", "author", "status", "bug_no"]
        fields = "__all__"  # 这个表示所有字段
        # exclude = ["bug_no"] #  排除字段
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 循环找到所有插件,添加了class: "from-control"
        for name, field in self.fields.items():
            field.widget.attrs = {"class": "form-control", "placeholder": field.label}
    # 钩子函数进行判重验证,这个名字注意是clean_加字段名
    def clean_number(self):
        tex_number = self.cleaned_data['number']
        exists = models.Case.objects.filter(number=tex_number).exists()
        if exists:
            raise ValidationError("用例编号已存在")
        return tex_number
def case_add(request):
    """新增用例(ModelForm方式)"""
    if request.method == 'GET':
        form = CaseModelForm()
        return render(request, 'case_add.html', {"form": form})
    # POST 请求提交的数据,数据校验
    form = CaseModelForm(data=request.POST)
    if form.is_valid():
        # 如果数据合法,这里判断的是所有字段不能为空,则存储到数据库
        # models.UserInfo.objects.create(..) 常规存储方式
        form.save()
        return redirect('/case/list')
    # 如果不满足if判断进入到else返回错误信息
    return render(request, 'case_add.html', {"form": form})
class CaseEditModelForm(forms.ModelForm):
    # 控制字段显示,但是不可编辑
    number = forms.CharField(disabled=True, label="用例编号")
    class Meta:
        model = models.Case
        fields = ["number", "name", "step", "expect", "actual", "priority", "author",  "status", "bug_no"]
        # fields = "__all__"  # 这个表示所有字段
        # exclude = ["bug_no"] #  排除字段
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 循环找到所有插件,添加了class: "from-control"
        for name, field in self.fields.items():
            field.widget.attrs = {"class": "form-control", "placeholder": field.label}
    # 钩子函数进行判重验证,这个名字注意是clean_加字段名
    def clean_number(self):
        # 获取当前编辑那一行的ID,从POST那里获取到了instance
        # print(self.instance.pk)
        tex_number = self.cleaned_data['number']
        exists = models.Case.objects.exclude(id=self.instance.pk).filter(number=tex_number).exists()
        if exists:
            raise ValidationError("用例编号已存在")
        return tex_number
def case_edit(request, nid):
    """编辑用户"""
    # 根据nid去数据库获取所在行数据
    row_object = models.Case.objects.filter(id=nid).first()
    if request.method == 'GET':
        form = CaseEditModelForm(instance=row_object)
        return  render(request, 'case_edit.html', {"form": form})
    # POST 请求提交的数据,数据校验
    form = CaseEditModelForm(data=request.POST, instance=row_object)
    if form.is_valid():
        # 如果数据合法,这里判断的是所有字段不能为空,则存储到数据库
        # models.UserInfo.objects.create(..) 常规存储方式
        # form.instance.字段名=值  # 如果需要存储用户输入之外的值使用这个
        form.save()
        return redirect('/case/list')
        # 如果不满足if判断进入到else返回错误信息
    return render(request, 'case_edit.html', {"form": form})
def case_delete(request, nid):
    """删除用例"""
    # 根据nid去数据库获取所在行数据进行删除
    models.Case.objects.filter(id=nid).delete()
    return redirect('/case/list')三、再优化,实现搜索+分页qing情况
知识点,点击分页时保留原来的查询条件
# -*- coding: utf-8 -*-
# @Time    : 2023/2/20 15:24
# @Author  : caicloud
# @File    : pagination.py
# @Software: PyCharm
# @Describe: 自定义分页组件
"""
这个翻页组件的使用说明(视图函数中):
def case_list(request):
    # 第一步查询数据
    case_set = models.Case.objects.all()
    # 第二步实例化封装的分页对象
    page_object = Pagination(request, case_set)
    context = {
        "case_set": page_object.page_queryset,  # 分完页的数据
        "page_string": page_object.html()  # 页码
    }
    return render(request, 'case_list.html', context)
html页面:
        <ul class="pagination">
            {{ page_string }}
        </ul>
"""
from django.utils.safestring import mark_safe
import copy
class Pagination(object):
    def __init__(self, request, queryset, page_size=10, page_param='page', plus=5):
        """
        :param request: 请求对象
        :param queryset: 查询符合条件的数据,根据这个数据进行分页处理
        :param page_size: 每页显示多少条数据
        :param page_param:在url中传递的获取分页的参数,例如/case/list/?page=5
        :param plus:显示当前页的前plus页 或后 plus页
        """
        # 以下是为了解决查询翻页查询条件保留的问题
        query_dict = copy.deepcopy(request.GET)
        query_dict._mutable = True
        self.query_dict = query_dict
        # ########################################
        self.page_param = page_param
        page = request.GET.get(self.page_param, "1")
        if page.isdecimal():
            page = int(page)
        else:
            page = 1
        self.page = page
        self.page_size = page_size
        self.start = (self.page - 1) * self.page_size
        self.end = self.page * self.page_size
        self.page_queryset = queryset[self.start: self.end]
        # 数据总条数
        total_count = queryset.count()
        total_page_count, div = divmod(total_count, page_size)
        if div:
            total_page_count += 1
        self.total_page_count = total_page_count
        self.plus = plus
    def html(self):
        # 计算出,线上当前页的前5页,后5页,plus=5
        # 数据未到达11页(数据量不达标时)
        if self.total_page_count <= 2 * self.plus + 1:
            start_page = 1
            end_page = self.total_page_count
        else:
            # 当前页< 5(前面的异常考虑)
            if self.page <= self.plus:
                start_page = 1
                end_page = 2 * self.plus + 1
            else:
                # 当前页+plus大于总页面(后面的异常考虑)
                if self.page + self.plus > self.total_page_count:
                    start_page = self.total_page_count - 2 * self.plus
                    end_page = self.total_page_count
                else:
                    # 前五页,后五页
                    start_page = self.page - self.plus
                    end_page = self.page + self.plus
        # 页码
        page_str_list = []
        self.query_dict.setlist(self.page_param, [1])
        # 首页
        page_str_list.append('<li><a href="?{}">首页</a></li>'.format(self.query_dict.urlencode()))
        # 上一页
        if self.page > 1:
            self.query_dict.setlist(self.page_param, [self.page - 1])
            prev = '<li><a href="?{}">上一页</a></li>'.format(self.query_dict.urlencode())
        else:
            self.query_dict.setlist(self.page_param, [1])
            prev = '<li><a href="?{}">上一页</a></li>'.format(self.query_dict.urlencode())
        page_str_list.append(prev)
        # 页面
        for i in range(start_page, end_page + 1):
            self.query_dict.setlist(self.page_param, [i])
            if i == self.page:
                ele = '<li class="active"><a href="?{}">{}</a></li>'.format(self.query_dict.urlencode(), i)
            else:
                ele = '<li><a href="?{}">{}</a></li>'.format(self.query_dict.urlencode(), i)
            page_str_list.append(ele)
        # 下一页
        if self.page < self.total_page_count:
            self.query_dict.setlist(self.page_param, [self.page + 1])
            prev = '<li><a href="?{}">下一页</a></li>'.format(self.query_dict.urlencode())
        else:
            self.query_dict.setlist(self.page_param, [self.total_page_count])
            prev = '<li><a href="?{}">下一页</a></li>'.format(self.query_dict.urlencode())
        page_str_list.append(prev)
        # 尾页
        self.query_dict.setlist(self.page_param, [self.total_page_count])
        page_str_list.append('<li><a href="?{}">尾页</a></li>'.format(self.query_dict.urlencode()))
        # 跳转
        search_string = """
            <li>
                <form style="float: left;margin-left: -1px" method="get">
                    <input name="page"
                           style="position: relative;float: left;display: inline-block;width: 80px;border-radius: 0"
                           type="text"  class="form-control" placeholder="页码">
                        <button style="border-radius: 0" class="btn btn-default" type="submit">跳转</button>
                    </span>
                </form>
            </li>
            """
        page_str_list.append(search_string)
        # 拼接html
        page_string = mark_safe("".join(page_str_list))
        return page_string四、优化其他查询页面实现分页和查询
将查询条件、分页在用户管理、部门管理实现

 
五、优化分页显示总页数和总条数
略




















