Python Web开发记录 Day10:Django part4 靓号管理与优化

名人说:莫道桑榆晚,为霞尚满天。------刘禹锡(刘梦得,诗豪)
创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊)

目录

在经过前两篇博客的学习,对Django开发管理模块能明显感到愈发熟练,本篇内容将继续增加管理模块,靓号管理,并在此之后对之前的所实现的模块整体进行一个优化,再往后的博客内容将陆续推展到管理员管理、账户登录、以及Ajax等内容。

1、数据库准备

1.首先在models.py中创建靓号表,定义数据表及属性字段。

python 复制代码
class PrettyNum(models.Model):
    """靓号表"""
    mobile = models.CharField(verbose_name="手机号", max_length=11)
    # 想要允许为空 加上null = True,black = True
    price = models.IntegerField(verbose_name="价格")

    level_choices = (
        (1, "1级"),
        (2, "2级"),
        (3, "3级"),
        (4, "4级"),
    )
    level = models.SmallIntegerField(verbose_name="级别", choices=level_choices, default=1)

    status_choices = (
        (1, "已占用"),
        (2, "未使用")
    )

    status = models.SmallIntegerField(verbose_name="状态", choices=status_choices, default=2)

2.之后在当前页面终端运行以下数据库转移命令

powershell 复制代码
python manage.py migrate
python manage.py makemigrations

之后运行本地服务器,启动Django项目,Django根据models中定义的内容就会帮咱们自动生成对应的数据表

powershell 复制代码
python manage.py runserver 

在用Django之前实现用户管理功能,我们先在mysql中写入几条数据:

sql 复制代码
insert into api_prettynum(mobile,price,level,status)values("111111111",19,1,1);

insert into api_prettynum(mobile,price,level,status)values("18888888888",20,1,1);

insert into api_prettynum(mobile,price,level,status)values("15555555555",22,1,1);

接下来去实现Django靓号管理。

2、靓号列表

同样地,我们采用与部门、用户列表同样的思路来实现靓号列表。

1.在urls.py 中添加靓号列表的路径pretty/list/,并告诉该路径指向的视图view.pretty_list

urls.py

python 复制代码
from django.urls import path
from api.views import depart,user,pretty

urlpatterns = [

    # 部门管理
    path("depart/list/", depart.depart_list),
    path("depart/add/", depart.depart_add),
    path("depart/delete/", depart.depart_delete),
    path("depart/<int:nid>/edit/", depart.depart_edit),

    # 用户管理
    path("user/list/", user.user_list),
    path("user/add/", user.user_add),
    path("user/model/form/add/", user.user_model_form_add),
    path('user/<int:nid>/edit/', user.user_edit),
    path("user/<int:nid>/delete/", user.user_delete),

    # 靓号管理
    path("pretty/list/", pretty.pretty_list),
]

2.在views.py 中写出对应的函数,发出请求,并返回响应pretty_list.html

views.py

python 复制代码
def pretty_list(request):
    """靓号列表"""

    data_dict = {}
    # 如果是空字典,表示获取所有
    # 不加后面的 "", 首次访问浏览器,搜索框中不会显示前端页面中的 placeholder="Search for..." 属性
    search_data = request.GET.get('query', "")
    if search_data:
        data_dict["mobile__contains"] = search_data

    queryset = PrettyNum.objects.filter(**data_dict).order_by("-level")

    ### 引入封装的 Pagination 类并初始化
    # 初始化
    page_object = Pagination(request, queryset, page_size=10, page_param="page")
    page_queryset = page_object.page_queryset

    # 调用对象的html方法,生成页码
    page_object.html()

    page_string = page_object.page_string
    head_page = page_object.head_page
    end_page = page_object.end_page

    context = {
        "pretty_data": page_queryset,  # 分页的数据
        "search_data": search_data,  # 搜索的内容
        "page_string": page_string,  # 页码
        "head_page": head_page,  # 首页
        "end_page": end_page,  # 尾页
    }

    return render(request, "pretty_list.html", context)

3.创建templates目录 下模版html文件pretty_list.html,以此定义靓号列表的网页结构和布局。

layout.html

html 复制代码
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!--Bootstrap框架-->
    <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.min.css' %}">
    <link rel="stylesheet" href="{% static 'plugins/font-awesome-4.7.0/css/font-awesome.css' %}">

    <!--datetimepicker插件-->
    <link rel="stylesheet" type="text/css"
          href="{% static 'plugins/bootstrap-datetimepicker/css/bootstrap-datetimepicker.css' %}">
    <style>
        .navbar {
            border-radius: 0;
        }
    </style>
    {% block css %}

    {% endblock %}
</head>
<body>
<nav class="navbar navbar-default">
    <div class="container">

        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                    data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">用户管理系统</a>
        </div>

        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
            <ul class="nav navbar-nav">
                <li><a href="/depart/list">部门管理</a></li>
                <li><a href="/user/list">用户管理</a></li>
                <li><a href="/pretty/list">靓号管理</a></li>

                <li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li>
            </ul>
            <ul class="nav navbar-nav navbar-right">
                <li><a href="#">登录</a></li>
                <li class="dropdown">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                       aria-expanded="false">张三 <span class="caret"></span></a>
                    <ul class="dropdown-menu">
                        <li><a href="#">个人资料</a></li>
                        <li><a href="#">我的信息</a></li>
                        <li><a href="#">Something else here</a></li>
                        <li role="separator" class="divider"></li>
                        <li><a href="#">注销</a></li>
                    </ul>
                </li>

            </ul>
        </div>
    </div>
</nav>

<div>
    <div class="container">
        {% block content %}{% endblock %}
    </div>
</div>

{% block js %}
    <script src="{% static 'js/jquery.min.js' %}"></script>
    <!-- 加载 Bootstrap DateTimePicker JS -->
    <script src="{% static 'plugins/bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.js' %}"></script>
    <script src="{% static 'plugins/bootstrap-datetimepicker/js/locales/bootstrap-datetimepicker.zh-CN.js' %}"></script>
    <script type="text/javascript">
    $(function () {
        //当容器加载完成,对容器调用工具函数
        $("#dt").datetimepicker({
            language: 'zh-CN', //语言
            format: 'yyyy-mm-dd',//日期的格式
            minView: 'month', //可以选择的最小视图
            initialDate: new Date(),//初始化显示的日期
            autoclose: true,//设置选择完日期或者时间之后,日否自动关闭日历
            todayBtn: true,//设置自动显示为今天
            clearBtn: false//设置是否清空按钮,默认为false
        });
    });
    </script>
{% endblock %}

pretty_list.html

html 复制代码
{% extends 'layout.html' %}

{% block content %}
<div class="container">

    <div>
        <div style="margin-bottom: 10px; ">
            <a class="btn btn-primary" href="/pretty/add/" target="_blank">新建靓号</a>

            <div style="float: right; width: 300px;">
                <form method="get">
                    <div class="input-group">
                        <input type="text" name="query" class="form-control" placeholder="Search for..."
                            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>

    <div>
        <div class="panel panel-default">
            <!-- Default panel contents -->
            <div class="panel-heading">
                <span class="glyphicon glyphicon-th-list" aria-hidden="true" style="margin-right: 5px;"></span>
                <span>靓号列表</span>
            </div>

            <!-- Table -->
            <table class="table table-bordered">
                <thead>
                    <tr>
                        <th>ID</th>
                        <th>号码</th>
                        <th>价格</th>
                        <th>级别</th>
                        <th>状态</th>
                        <th>操作</th>
                    </tr>
                </thead>
                <tbody>
                    {% for obj in pretty_data %}
                    <tr>
                        <th>{{ obj.id }}</th>
                        <td>{{ obj.mobile }}</td>
                        <td>{{ obj.price }}</td>
                        <td>{{ obj.get_level_display }}</td>
                        <td>{{ obj.get_status_display }}</td>
                        <td>
                            <a class="btn btn-primary btn-xs" href="/pretty/{{ obj.id }}/edit/">编辑</a>
                            <a class="btn btn-danger btn-xs" href="/pretty/{{ obj.id }}/delete/">删除</a>
                        </td>
                    </tr>
                    {% endfor %}
                </tbody>
            </table>
        </div>
    </div>
</div>
        <div style="display: flex; align-items: center">
            <!-- 页面切换 -->
            <div> <!-- 无需再设置float和宽度 -->
                <ul class="pagination">
                    {{ page_string }}
                </ul>
            </div>
            <!-- 输入页码框 -->
            <div style="width: 200px;margin-bottom: 5px"> <!-- 保持输入框宽度,但不需要float:right -->
                <form method="get">
                    <div class="input-group">
                        <input type="text" name="page" class="form-control" placeholder="页码">
                        <span class="input-group-btn">
                    <button class="btn btn-default" type="submit">跳转</button>
                </span>
                    </div>
                </form>
            </div>
        </div>
{% endblock %}

效果:

3、新建靓号

同样地采用ModelForm组件来实现。

1.在urls.py 中添加新建靓号的路径pretty/add/,并告诉该路径指向的视图view.pretty_add

urls.py

python 复制代码
 from django.urls import path
 from api.views import depart,user,pretty
 
 urlpatterns = [
    # 部门管理
    path("depart/list/", depart.depart_list),
    path("depart/add/", depart.depart_add),
    path("depart/delete/", depart.depart_delete),
    path("depart/<int:nid>/edit/", depart.depart_edit),

    # 用户管理
    path("user/list/", user.user_list),
    path("user/add/", user.user_add),
    path("user/model/form/add/", user.user_model_form_add),
    path('user/<int:nid>/edit/', user.user_edit),
    path("user/<int:nid>/delete/", user.user_delete),

    # 靓号管理
    path("pretty/list/", pretty.pretty_list),
    path("pretty/add/", pretty.pretty_add),
 ]
 

2.在views.py 中写出对应的函数,发出请求,并返回响应pretty_add.html

views.py

python 复制代码
class PrettyModelForm(BootStrapModelForm):
    # 验证:方式1
    mobile = forms.CharField(
        label="手机号",
        validators=[RegexValidator(r'^1[3-9]\d{9}$', '手机号格式错误'), ],
    )

    class Meta:
        model = models.PrettyNum
        # fields = "__all__"
        # exclude = ['level']
        fields = ["mobile", 'price', 'level', 'status']

    # 验证:方式2
    def clean_mobile(self):
        txt_mobile = self.cleaned_data["mobile"]

        exists = models.PrettyNum.objects.filter(mobile=txt_mobile).exists()
        if exists:
            raise ValidationError("手机号已存在")

        # 验证通过,用户输入的值返回
        return txt_mobile
    
def pretty_add(request):
    """添加靓号"""
    if request.method == "GET":
        form = PrettyModelForm()
        return render(request, "pretty_add.html", {"form": form})
    form = PrettyModelForm(data=request.POST)
    if form.is_valid():
        form.save()
        return redirect('/pretty/list/')
    return render(request, 'pretty_add.html', {"form": form})

3.创建templates目录 下模版html文件pretty_add.html,以此实现靓号信息的新增。

pretty_add.html

html 复制代码
{% extends 'layout.html' %}

{% block content %}
    <div class="container">
        <div class="panel panel-default">
            <div class="panel-heading">
                <h3 class="panel-title" style="font-weight: bold">新建靓号</h3>
            </div>
            <div class="panel-body">
                <form method="post" novalidate>
                    {% csrf_token %}

                    {% for field in form %}
                        <div class="form-group">
                            <label>{{ field.label }}</label>
                            {{ field }}
                            <span style="color: red;">{{ field.errors.0 }}</span>
                        </div>
                    {% endfor %}
                    <div class="form-group">
                        <div class="col-sm-offset-2 col-sm-10">
                            <button type="submit" class="btn btn-primary">提 交</button>
                        </div>
                    </div>
                </form>
            </div>
        </div>
    </div>
{% endblock %}
<form method="post">
    {% csrf_token %}
    {% for field in form %}
        {{ field.label }} : {{ field }}
    {% endfor %}
</form>

效果:

4、编辑靓号

在完成新建靓号后我们来接着实现编辑靓号。

①编辑靓号

1.在urls.py 中增加编辑靓号的路径pretty/<int:nid>/edit/,并告诉该路径指向的视图view.pretty_edit

urls.py

python 复制代码
 from django.urls import path
 from api.views import depart,user,pretty
 
 urlpatterns = [
    # 部门管理
    path("depart/list/", depart.depart_list),
    path("depart/add/", depart.depart_add),
    path("depart/delete/", depart.depart_delete),
    path("depart/<int:nid>/edit/", depart.depart_edit),

    # 用户管理
    path("user/list/", user.user_list),
    path("user/add/", user.user_add),
    path("user/model/form/add/", user.user_model_form_add),
    path('user/<int:nid>/edit/', user.user_edit),
    path("user/<int:nid>/delete/", user.user_delete),

    # 靓号管理
    path("pretty/list/", pretty.pretty_list),
    path("pretty/add/", pretty.pretty_add),
    path("pretty/<int:nid>/edit/", pretty.pretty_edit),
 ]
 

2.在views.py 中写出对应的函数,发出请求,并返回响应pretty_edit.html

views.py

python 复制代码
def pretty_edit(request, nid):
    """ 编辑靓号 """
    row_object = models.PrettyNum.objects.filter(id=nid).first()

    if request.method == "GET":
        form = PrettyEditModelForm(instance=row_object)
        return render(request, 'pretty_edit.html', {"form": form})

    form = PrettyEditModelForm(data=request.POST, instance=row_object)
    if form.is_valid():
        form.save()
        return redirect('/pretty/list/')

    return render(request, 'pretty_edit.html', {"form": form})

3.创建templates目录 下模版html文件pretty_edit.html,以此实现靓号信息的编辑。

pretty_edit.html

html 复制代码
{% extends 'layout.html' %}

{% block content %}
    <div class="container">
        <div class="panel panel-default">
            <div class="panel-heading">
                <h3 class="panel-title" style="font-weight: bold">编辑靓号</h3>
            </div>
            <div class="panel-body">
                <form method="post" novalidate>
                    {% csrf_token %}

                    {% for field in form %}
                        <div class="form-group">
                            <label>{{ field.label }}</label>
                            {{ field }}
                            <span style="color: red;">{{ field.errors.0 }}</span>
                        </div>
                    {% endfor %}
                    <div class="form-group">
                        <div class="col-sm-offset-2 col-sm-10">
                            <button type="submit" class="btn btn-primary">提 交</button>
                        </div>
                    </div>
                </form>
            </div>
        </div>
    </div>
{% endblock %}

效果:

②手机号重复问题

在添加和编辑时,遇到手机号重复的我们应该不能再进行添加或编辑,因此我们可以使用以下方式来解决手机号重复问题,对于:

  • 添加:若手机号已经存在,提示"手机号已存在"。

  • 编辑:若手机号及当前手机号本身以外已存在,提示"手机号已存在"。

我们可以考虑在class PrettyModelForm也就是靓号ModelForm类里添加以下代码:

python 复制代码
def clean_mobile(self):
    txt_mobile = self.cleaned_data['mobile']

    if len(txt_mobile) != 11:
        # 验证不通过
        raise ValidationError('格式错误')

    exists_data = PrettyNum.objects.filter(mobile=txt_mobile).exists()
    if exists_data:
        raise ValidationError("手机号已存在")

    # 验证通过
    return txt_mobile

整合之后,可以这样写:

python 复制代码
class PrettyModelForm(BootStrapModelForm):
    # 验证:方式1
    mobile = forms.CharField(
        label="手机号",
        validators=[RegexValidator(r'^1[3-9]\d{9}$', '手机号格式错误'), ],
    )

    class Meta:
        model = models.PrettyNum
        # fields = "__all__"
        # exclude = ['level']
        fields = ["mobile", 'price', 'level', 'status']

    # 验证:方式2
    def clean_mobile(self):
        txt_mobile = self.cleaned_data["mobile"]

        exists = models.PrettyNum.objects.filter(mobile=txt_mobile).exists()
        if exists:
            raise ValidationError("手机号已存在")

        # 验证通过,用户输入的值返回
        return txt_mobile

效果:

5、删除靓号

与上述步骤相仿,我们继续来实现删除靓号,删除靓号的基础是建立在靓号列表的实现和新增靓号的功能之上的。

1.在urls.py 中添加删除靓号的路径pretty/<int:nid>/delete/,并告诉该路径指向的视图view.pretty_delete

urls.py

python 复制代码
 from django.urls import path
 from api.views import depart,user,pretty
 
 urlpatterns = [
    # 部门管理
    path("depart/list/", depart.depart_list),
    path("depart/add/", depart.depart_add),
    path("depart/delete/", depart.depart_delete),
    path("depart/<int:nid>/edit/", depart.depart_edit),

    # 用户管理
    path("user/list/", user.user_list),
    path("user/add/", user.user_add),
    path("user/model/form/add/", user.user_model_form_add),
    path('user/<int:nid>/edit/', user.user_edit),
    path("user/<int:nid>/delete/", user.user_delete),

    # 靓号管理
    path("pretty/list/", pretty.pretty_list),
    path("pretty/add/", pretty.pretty_add),
    path("pretty/<int:nid>/edit/", pretty.pretty_edit),
    path("pretty/<int:nid>/delete/", pretty.pretty_delete),
 ]
 

2.在views.py 中写出对应的函数,发出请求,并返回响应pretty_list.html

views.py

python 复制代码
def pretty_delete(request, nid):
    models.PrettyNum.objects.filter(id=nid).delete()
    return redirect('/pretty/list/')

3.操作templates目录 下模版html文件user_list.html,以此实现靓号信息的删除。

pretty_list.html

html 复制代码
{% extends 'layout.html' %}

{% block content %}
<div class="container">

    <div>
        <div style="margin-bottom: 10px; ">
            <a class="btn btn-primary" href="/pretty/add/" target="_blank">新建靓号</a>

            <div style="float: right; width: 300px;">
                <form method="get">
                    <div class="input-group">
                        <input type="text" name="query" class="form-control" placeholder="Search for..."
                            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>

    <div>
        <div class="panel panel-default">
            <!-- Default panel contents -->
            <div class="panel-heading">
                <span class="glyphicon glyphicon-th-list" aria-hidden="true" style="margin-right: 5px;"></span>
                <span>靓号列表</span>
            </div>

            <!-- Table -->
            <table class="table table-bordered">
                <thead>
                    <tr>
                        <th>ID</th>
                        <th>号码</th>
                        <th>价格</th>
                        <th>级别</th>
                        <th>状态</th>
                        <th>操作</th>
                    </tr>
                </thead>
                <tbody>
                    {% for obj in pretty_data %}
                    <tr>
                        <th>{{ obj.id }}</th>
                        <td>{{ obj.mobile }}</td>
                        <td>{{ obj.price }}</td>
                        <td>{{ obj.get_level_display }}</td>
                        <td>{{ obj.get_status_display }}</td>
                        <td>
                            <a class="btn btn-primary btn-xs" href="/pretty/{{ obj.id }}/edit/">编辑</a>
                            <a class="btn btn-danger btn-xs" href="/pretty/{{ obj.id }}/delete/">删除</a>
                        </td>
                    </tr>
                    {% endfor %}
                </tbody>
            </table>
        </div>
    </div>
</div>
        <div style="display: flex; align-items: center">
            <!-- 页面切换 -->
            <div> <!-- 无需再设置float和宽度 -->
                <ul class="pagination">
                    {{ page_string }}
                </ul>
            </div>
            <!-- 输入页码框 -->
            <div style="width: 200px;margin-bottom: 5px"> <!-- 保持输入框宽度,但不需要float:right -->
                <form method="get">
                    <div class="input-group">
                        <input type="text" name="page" class="form-control" placeholder="页码">
                        <span class="input-group-btn">
                    <button class="btn btn-default" type="submit">跳转</button>
                </span>
                    </div>
                </form>
            </div>
        </div>
{% endblock %}

效果:

整体效果:

6、靓号搜索

在实现了上述基础模块功能后,新增一个搜索的功能,那该怎么实现呢?

filter()筛选方法可以帮助我们实现,例如:

python 复制代码
# models.PrettyNum.objects.filter()用于从数据库中查询符合特定条件的记录
models.PrettyNum.objects.filter("18812345678",id=123)

data_dict = {"mobile":"18821213246","id"=123}
models.PrettyNum.objects.filter(**data_dict)
python 复制代码
# 等于12
models.PrettyNum.objects.filter(id=12)
# 大于12
models.PrettyNum.objects.filter(id__gt=12)
# 大于等于12
models.PrettyNum.objects.filter(id__gte=12)
# 小于12
models.PrettyNum.objects.filter(id__lt=12)
# 小于等于12
models.PrettyNum.objects.filter(id__lte=12)

data_dict = {"id__lte":12}
models.PrettyNum.objects.filter(**data_dict)
python 复制代码
# 筛选出等于152的
models.PrettyNum.objects.filter(mobile="152")
# 筛选出以152开头的
models.PrettyNum.objects.filter(mobile__startwith="152")
# 筛选出以765结尾的
models.PrettyNum.objects.filter(mobile__endswith="765")
# 筛选出包含666的
models.PrettyNum.objects.filter(mobile__contains="666")

data_dict = {"mobile_contains":"999"}
models.PrettyNum.objects.filter(**data_dict)

紧接着我们来实现这个功能:

修改views.py视图函数中的pretty_list:

python 复制代码
def pretty_list(request):
    """靓号列表"""

    data_dict = {}
    # 如果是空字典,表示获取所有
    # 不加后面的 "", 首次访问浏览器,搜索框中不会显示前端页面中的 placeholder="Search for..." 属性
    search_data = request.GET.get('query', "")
    if search_data:
        data_dict["mobile__contains"] = search_data

    queryset = PrettyNum.objects.filter(**data_dict).order_by("-level")

    ### 引入封装的 Pagination 类并初始化
    # 初始化
    page_object = Pagination(request, queryset, page_size=10, page_param="page")
    page_queryset = page_object.page_queryset

    # 调用对象的html方法,生成页码
    page_object.html()

    page_string = page_object.page_string
    head_page = page_object.head_page
    end_page = page_object.end_page

    context = {
        "pretty_data": page_queryset,  # 分页的数据
        "search_data": search_data,  # 搜索的内容
        "page_string": page_string,  # 页码
        "head_page": head_page,  # 首页
        "end_page": end_page,  # 尾页
    }

    return render(request, "pretty_list.html", context)

修改pretty_list.html:

python 复制代码
{% extends 'layout.html' %}

{% block content %}
<div class="container">

    <div>
        <div style="margin-bottom: 10px; ">
            <a class="btn btn-primary" href="/pretty/add/" target="_blank">新建靓号</a>

            <div style="float: right; width: 300px;">
                <form method="get">
                    <div class="input-group">
                        <input type="text" name="query" class="form-control" placeholder="Search for..."
                            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>

    <div>
        <div class="panel panel-default">
            <!-- Default panel contents -->
            <div class="panel-heading">
                <span class="glyphicon glyphicon-th-list" aria-hidden="true" style="margin-right: 5px;"></span>
                <span>靓号列表</span>
            </div>

            <!-- Table -->
            <table class="table table-bordered">
                <thead>
                    <tr>
                        <th>ID</th>
                        <th>号码</th>
                        <th>价格</th>
                        <th>级别</th>
                        <th>状态</th>
                        <th>操作</th>
                    </tr>
                </thead>
                <tbody>
                    {% for obj in pretty_data %}
                    <tr>
                        <th>{{ obj.id }}</th>
                        <td>{{ obj.mobile }}</td>
                        <td>{{ obj.price }}</td>
                        <td>{{ obj.get_level_display }}</td>
                        <td>{{ obj.get_status_display }}</td>
                        <td>
                            <a class="btn btn-primary btn-xs" href="/pretty/{{ obj.id }}/edit/">编辑</a>
                            <a class="btn btn-danger btn-xs" href="/pretty/{{ obj.id }}/delete/">删除</a>
                        </td>
                    </tr>
                    {% endfor %}
                </tbody>
            </table>
        </div>
    </div>
</div>
        <div style="display: flex; align-items: center">
            <!-- 页面切换 -->
            <div> <!-- 无需再设置float和宽度 -->
                <ul class="pagination">
                    {{ page_string }}
                </ul>
            </div>
            <!-- 输入页码框 -->
            <div style="width: 200px;margin-bottom: 5px"> <!-- 保持输入框宽度,但不需要float:right -->
                <form method="get">
                    <div class="input-group">
                        <input type="text" name="page" class="form-control" placeholder="页码">
                        <span class="input-group-btn">
                    <button class="btn btn-default" type="submit">跳转</button>
                </span>
                    </div>
                </form>
            </div>
        </div>
{% endblock %}

效果:

7、分页切换

要想实现分页跳转切换,要明白它的原理,分页首先要得出总页数,划分好每页的数量,比如10或20,拿总页数除以10或20,最终得到页数,然后再设计跳转的功能,此时比如page=1到page=2,此时我们可以通过操作点击对应的页码,进而实现page+1来实现。在具体实现它之前先了解一些简要的操作,假如每页10个,我们该如何将这10个提取出来?

python 复制代码
queryset = models.PrettyNum.objects.all()

queryset = models.PrettyNum.objects.filter(id=1)[0:10]
# 第1页
queryset = models.PrettyNum.objects.all()[0:10]
# 第2页
queryset = models.PrettyNum.objects.all()[10:20]
# 第3页
queryset = models.PrettyNum.objects.all()[20:30]
python 复制代码
data = models.PrettyNum.objects.all().count()
data = models.PrettyNum.objects.filter(id=1).count()
  • 分页的逻辑和处理规则
python 复制代码
def pretty_list(request):
    # """靓号列表"""

    # for i in range(300):
    #     models.PrettyNum.objects.create(mobile="18811899006",price=10,level=1,status=1)

    data_dict = {}
    search_data = request.GET.get("q", "")

    if search_data:
        data_dict["mobile__contains"] = search_data


    queryset = models.PrettyNum.objects.filter(**data_dict).order_by("-level")

    page_object = Pagination(request)
    page_queryset = page_object.page_queryset

    # 1.根据用户想要访问的页码,计算出起始和终止位置
    # page = int(request.GET.get('page', 1))
    # page_size = 10  # 每页显示10条数据
    # start = (page - 1) * page_size
    # end = page * page_size

    # 数据总条数
    total_count = models.PrettyNum.objects.filter(**data_dict).order_by("-level").count()

    # 总页码
    total_page_count, div = divmod(total_count, page_size)
    if div:
        total_page_count += 1

    # 计算出,显示出当前页的前5页和后5页
    plus = 5
    if total_page_count <= 2 * plus + 1:
        # 数据库中的数据比较少,都没有达到11页
        start_page = 1
        end_page = total_page_count
    else:
        # 数据库中的数据较多,大于11页

        # 当前页小于5时(小极值)
        if page <= plus:
            start_page = 1
            end_page = 2 * plus + 1
        else:
            # 当前页大于5时
            # 当前页+5 > 总页面
            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 + 1

    # 页码
    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))

    page_string = mark_safe("".join(page_str_list))

    page_number = request.GET.get('page', 1)  # 获取页码,默认为1
    paginator = Paginator(queryset, 10)  # 假设每页显示10条数据

    try:
        page_obj = paginator.page(page_number)
    except PageNotAnInteger:
        # 如果页码不是一个整数,则显示第一页
        page_obj = paginator.page(1)
    except EmptyPage:
        # 如果页码超出范围,则显示最后一页
        page_obj = paginator.page(paginator.num_pages)

    search_string = """"""
    page_str_list.append(search_string)

    return render(request, 'pretty_list.html',
                  {'queryset': queryset, "search_data": search_data, "page_string": page_string,'page_obj': page_obj})

在实现了上述逻辑之后,虽然代码难度不高,但是难免过于繁琐,那么该怎么灵活地利用它呢?

可以对它分页类封装,这样以后再遇到要写分页的时候,我们只需要调用它稍加修改就好:

新建文件夹utils,并新建pagination.py文件以存放它:

pagination.py

python 复制代码
"""
自定义的分页组件

"""

from django.utils.safestring import mark_safe
import copy


class Pagination(object):
    def __init__(self, request, queryset, page_size=10, page_param="page", page_show=5):
        """
        :param request: 请求的对象
        :param queryset: 符合条件的数据(根据此数据进行分页处理)
        :param page_size: 每页显示多少条数据
        :param page_param: 获取在URL中传递的分页参数, 例如: /pretty/list/?page=21
        :param page_show: 页码显示前几页后几页
        """

        # 防止搜索出结果进行翻页时,URL参数没有了搜索参数
        query_dict = copy.deepcopy(request.GET)
        query_dict._mutable = True
        self.query_dict = query_dict

        self.page_param = page_param

        page = int(request.GET.get(page_param, 1))

        # 如果不是整数
        if type(page) != int:
            # 强制让页码为1
            page = 1

        self.page = page

        self.start = (page - 1) * page_size
        self.end = page * page_size

        # 每页展示的数据行数
        self.page_queryset = queryset[self.start:self.end]

        total_data_count = queryset.count()     # 数据行数
        total_page_count, div = divmod(total_data_count, page_size)
        if div:
            total_page_count += 1
        self.total_page_count = total_page_count    # 总页码数量
        self.page_show = page_show  # 当前页前后展示的页码数量
        self.request = request

    def html(self):
        # 如果总页码数量大于 11
        if self.total_page_count > self.page_show * 2 + 1:
            # 如果当前页面页码位置小于等于5
            if self.page <= 5:
                start_page = 1
                end_page = self.page_show * 2 + 2
            # 否则,当前页面页码位置大于5时
            else:
                # 防止页码超出范围
                if self.page >= self.total_page_count - self.page_show:
                    start_page = self.total_page_count - self.page_show * 2
                    end_page = self.total_page_count + 1
                else:
                    # 计算出当前页的前5页和后5页
                    start_page = self.page - self.page_show
                    end_page = self.page + self.page_show + 1

        else:
            start_page = 1
            end_page = self.total_page_count + 1

        ######## 创建页码 ########
        # 页码
        page_str_list = []

        # self.query_dict.setlist(self.page_param, [1])
        # page_str_list.append('<li><a href="?page={}">{}</a></li>'.format(self.query_dict.urlencode()))

        # 跳到首页
        self.query_dict.setlist(self.page_param, [1])
        self.head_page = '<li><a href="?{}" aria-label="Previous"><span aria-hidden="true">首页</span></a></li>'.format(self.query_dict.urlencode())
        page_str_list.append(self.head_page)

        # 跳到上10页
        # 如果当前页面小于 11, 防止超过最小页数
        if self.page < self.page_show * 2 + 1:
            self.query_dict.setlist(self.page_param, [1])
            prev = '<li><a href="?{}">{}</a></li>'.format(
                self.query_dict.urlencode(), "<<")
            page_str_list.append(prev)
        else:
            self.query_dict.setlist(self.page_param, [self.page - 10])
            prev = '<li><a href="?{}">{}</a></li>'.format(
                self.query_dict.urlencode(), "<<")
            page_str_list.append(prev)

        for i in range(start_page, end_page):
            # 如果是当前页,高亮显示页码颜色
            if self.page == i:
                self.query_dict.setlist(self.page_param, [i])
                ele = '<li class="active"><a href="?{}">{}</a></li>'.format(
                    self.query_dict.urlencode(), i)
            else:
                self.query_dict.setlist(self.page_param, [i])
                ele = '<li><a href="?{}">{}</a></li>'.format(
                    self.query_dict.urlencode(), i)
            page_str_list.append(ele)

        # 跳到下10页
        # 如果当前页面页数 大于 最大页面数量减去(page_show*2+1),则直接跳到最后一页,防止超过最大页数
        if self.page >= self.total_page_count - self.page_show * 2 + 1:
            self.query_dict.setlist(self.page_param, [self.total_page_count])
            next = '<li><a href="?{}">{}</a></li>'.format(
                self.query_dict.urlencode(), ">>")
            page_str_list.append(next)
        else:
            self.query_dict.setlist(self.page_param, [self.page + 10])
            next = '<li><a href="?page={}">{}</a></li>'.format(
                self.query_dict.urlencode(), ">>")
            page_str_list.append(next)

        # 跳到尾页
        self.query_dict.setlist(self.page_param, [self.total_page_count])
        self.end_page = '<li><a href="?{}" aria-label="Next"><span aria-hidden="true">尾页</span></a></li>'.format(self.query_dict.urlencode())
        page_str_list.append(self.end_page)

        self.page_string = mark_safe("".join(page_str_list))
8、优化(整合分类)

①拆分views.py

由上面可以看到,上面部分内容有些杂乱,因此我们可以将views.py 换成 views目录,将views.py拆成depart.pyuser.pypretty.py,这样区分更鲜明,更易于管理

1.depart.py

python 复制代码
# -*- coding: utf-8 -*-            
# @Time : 2024-03-13 21:39
from django.shortcuts import render, redirect
from api import models
from api.utils.pagination import Pagination


def depart_list(request):
    """部门列表"""
    queryset = models.Department.objects.all()

    page_object = Pagination(request, queryset, page_size=2)
    page_object.html()

    context = {
        "queryset": page_object.page_queryset,
        "page_string": page_object.page_string,
    }
    return render(request, 'depart_list.html', context)

def depart_add(request):
    """添加部门"""
    if request.method == "GET":
        return render(request, 'depart_add.html')

    # 获取用户POST提交过来的数据(title输入为空)
    title = request.POST.get("title")

    # 保存到数据库
    models.Department.objects.create(title=title)

    # 重定向回部门列表
    return redirect("/depart/list/")

def depart_delete(request):
    """删除部门"""
    # 获取ID
    # http://127.0.0.1:8000/depart/delete/?nid=1
    nid = request.GET.get('nid')

    models.Department.objects.filter(id=nid).delete()
    # 重定向回部门列表
    return redirect("/depart/list")

def depart_edit(request, nid):
    """修改部门"""
    if request.method == "GET":
        # 根据nid,获取他的数据[obj,]
        row_object = models.Department.objects.filter(id=nid).first()
        return render(request, 'depart_edit.html', {"row_object": row_object})
    # 获取用户的标题
    title = request.POST.get("title")
    # 根据ID找到数据库中的数据进行更新
    models.Department.objects.filter(id=nid).update(title=title)
    # 重定向回部门列表
    return redirect("/depart/list/")

2.user.py

python 复制代码
# -*- coding: utf-8 -*-            
# @Time : 2024-03-13 21:39
from django.shortcuts import render, redirect
from api import models
from api.models import UserInfo
from api.utils.pagination import Pagination
from api.utils.form import UserModelForm


def user_list(request):
    """用户列表"""
    # 获取所有用户列表
    queryset = UserInfo.objects.all()

    page_object = Pagination(request, queryset, page_size=3)

    # 用 python 的语法获取数据
    """
    for obj in user_data:
        # obj.get_gender_display() 表示匹配 男/女,原始字段名为gender,obj.get_字段名称_display()
        # obj.create_time.strftime("%Y-%m-%d") 表示将时间格式转换成固定格式的字符串
        # obj.depart.title 表示获取depart_id对应的部门名称,因为我们在models中定义表时与另外一张表设置了级联关系,有外键
        print(obj.id, obj.name, obj.password, obj.age, obj.account, obj.get_gender_display(), obj.depart.title, obj.create_time.strftime("%Y-%m-%d"))
    """

    page_object.html()

    context = {
        "queryset": page_object.page_queryset,
        "page_string": page_object.page_string,
    }

    return render(request, "user_list.html", context)


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 = request.POST.get('name')
    pwd = request.POST.get('pwd')
    age = request.POST.get('age')
    account = request.POST.get('ac')
    ctime = request.POST.get('ctime')
    gender = request.POST.get('gd')
    depart_id = request.POST.get('dp')

    # 添加到数据库中国
    models.UserInfo.objects.create(name=user, password=pwd, age=age, account=account, creat_time=ctime, gender=gender,
                                   depart_id=depart_id)

    # 返回到用户列表页面
    return redirect("/user/list/")


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():
        print(form.cleaned_data)
        # 直接保存至数据库
        form.save()
        return redirect("/user/list/")

    # 校验失败(在页面上显示错误信息)
    return render(request, "user_model_form_add.html", {"form": form})


def user_edit(request, nid):
    """编辑用户"""
    row_obj = UserInfo.objects.filter(id=nid).first()

    # GET请求
    if request.method == "GET":
        form = UserModelForm(instance=row_obj)
        return render(request, "user_edit.html", {"form": form})

    # POST请求
    form = UserModelForm(data=request.POST, instance=row_obj)
    if form.is_valid():
        form.save()
        return redirect("/user/list/")

    return render(request, "user_edit.html", {"form": form})


def user_delete(request, nid):
    models.UserInfo.objects.filter(id=nid).delete()
    return redirect('/user/list/')

3.pretty.py

python 复制代码
# -*- coding: utf-8 -*-            
# @Time : 2024-03-13 21:39
from django import forms
from django.core.validators import RegexValidator
from django.shortcuts import render, redirect
from api import models
from api.models import PrettyNum, UserInfo
from api.utils.pagination import Pagination
from api.utils.form import UserModelForm, PrettyModelForm, PrettyEditModelForm


def pretty_list(request):
    """靓号列表"""

    data_dict = {}
    # 如果是空字典,表示获取所有
    # 不加后面的 "", 首次访问浏览器,搜索框中不会显示前端页面中的 placeholder="Search for..." 属性
    search_data = request.GET.get('query', "")
    if search_data:
        data_dict["mobile__contains"] = search_data

    queryset = PrettyNum.objects.filter(**data_dict).order_by("-level")

    ### 引入封装的 Pagination 类并初始化
    # 初始化
    page_object = Pagination(request, queryset, page_size=10, page_param="page")
    page_queryset = page_object.page_queryset

    # 调用对象的html方法,生成页码
    page_object.html()

    page_string = page_object.page_string
    head_page = page_object.head_page
    end_page = page_object.end_page

    context = {
        "pretty_data": page_queryset,  # 分页的数据
        "search_data": search_data,  # 搜索的内容
        "page_string": page_string,  # 页码
        "head_page": head_page,  # 首页
        "end_page": end_page,  # 尾页
    }

    return render(request, "pretty_list.html", context)


def pretty_add(request):
    """添加靓号"""
    if request.method == "GET":
        form = PrettyModelForm()
        return render(request, "pretty_add.html", {"form": form})
    form = PrettyModelForm(data=request.POST)
    if form.is_valid():
        form.save()
        return redirect('/pretty/list/')
    return render(request, 'pretty_add.html', {"form": form})


def pretty_edit(request, nid):
    """ 编辑靓号 """
    row_object = models.PrettyNum.objects.filter(id=nid).first()

    if request.method == "GET":
        form = PrettyEditModelForm(instance=row_object)
        return render(request, 'pretty_edit.html', {"form": form})

    form = PrettyEditModelForm(data=request.POST, instance=row_object)
    if form.is_valid():
        form.save()
        return redirect('/pretty/list/')

    return render(request, 'pretty_edit.html', {"form": form})


def pretty_delete(request, nid):
    models.PrettyNum.objects.filter(id=nid).delete()
    return redirect('/pretty/list/')

新手保护期,views可拆分,尽量先别拆models.py,一般平时够用。

②ModelForm优化

将三个单独定义的类

python 复制代码
class UserModelForm(forms.ModelForm):
class PrettyModelForm(forms.ModelForm):
class PrettyEditModelForm(forms.ModelForm):

改为继承自同一个自定义类,这样做的好处在于,以后再有其它类,常规的部分不需要再重复定义,直接从父类获取即可:

python 复制代码
class UserModelForm(BootStrapModelForm):
class PrettyModelForm(BootStrapModelForm):
class PrettyEditModelForm(BootStrapModelForm):

将自定义类放在utils目录下新建的bootstrap.py中:

python 复制代码
class BootStrapModelForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 循环ModelForm中的所有字段,给每个字段的插件设置
        for name, field in self.fields.items():
            # 字段中有属性,保留原来的属性,没有属性,才增加。
            if field.widget.attrs:
                field.widget.attrs["class"] = "form-control"
                field.widget.attrs["placeholder"] = field.label
            else:
                field.widget.attrs = {
                    "class": "form-control",
                    "placeholder": field.label
                }

③整合Form

将三个类整合到一起,放在utils目录下新建的form.py中

python 复制代码
from api import models
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
from django import forms
from api.utils.bootstrap import BootStrapModelForm


class UserModelForm(BootStrapModelForm):
    name = forms.CharField(
        min_length=3,
        label="用户名",
        widget=forms.TextInput(attrs={"class": "form-control"})
    )

    class Meta:
        model = models.UserInfo
        fields = ["name", "password", "age", 'account', 'creat_time', "gender", "depart"]


class PrettyModelForm(BootStrapModelForm):
    # 验证:方式1
    mobile = forms.CharField(
        label="手机号",
        validators=[RegexValidator(r'^1[3-9]\d{9}$', '手机号格式错误'), ],
    )

    class Meta:
        model = models.PrettyNum
        # fields = "__all__"
        # exclude = ['level']
        fields = ["mobile", 'price', 'level', 'status']

    # 验证:方式2
    def clean_mobile(self):
        txt_mobile = self.cleaned_data["mobile"]

        exists = models.PrettyNum.objects.filter(mobile=txt_mobile).exists()
        if exists:
            raise ValidationError("手机号已存在")

        # 验证通过,用户输入的值返回
        return txt_mobile


class PrettyEditModelForm(BootStrapModelForm):
    # mobile = forms.CharField(disabled=True, label="手机号")
    mobile = forms.CharField(
        label="手机号",
        validators=[RegexValidator(r'^1[3-9]\d{9}$', '手机号格式错误'), ],
    )

    class Meta:
        model = models.PrettyNum
        fields = ['mobile', 'price', 'level', 'status']

    # 验证:方式2
    def clean_mobile(self):
        # 当前编辑的哪一行的ID
        # print(self.instance.pk)
        txt_mobile = self.cleaned_data["mobile"]
        exists = models.PrettyNum.objects.exclude(id=self.instance.pk).filter(mobile=txt_mobile).exists()
        if exists:
            raise ValidationError("手机号已存在")

        # 验证通过,用户输入的值返回
        return txt_mobile

效果:

优化后,仍然能达到相同的效果,区别在于,后者更加整齐,分类更清晰。

关于靓号管理的实现就到这里,后续会继续实现管理员管理、账户登录、以及Ajax等,并及时更新博客,敬请期待。

很感谢你能看到这里,如有相关疑问,还请下方评论留言。
Code_流苏(CSDN) (一个喜欢古诗词和编程的Coder😊)
希望本篇内容能对大家有所帮助,如果大家喜欢的话,请动动手点个赞和关注吧,非常感谢你们的支持!

相关推荐
PieroPc21 分钟前
Python 自动化 打开网站 填表登陆 例子
运维·python·自动化
m0_7482550229 分钟前
前端常用算法集合
前端·算法
真的很上进43 分钟前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
web130933203981 小时前
vue elementUI form组件动态添加el-form-item并且动态添加rules必填项校验方法
前端·vue.js·elementui
VinciYan1 小时前
基于Jenkins+Docker的自动化部署实践——整合Git与Python脚本实现远程部署
python·ubuntu·docker·自动化·jenkins·.net·运维开发
NiNg_1_2341 小时前
Echarts连接数据库,实时绘制图表详解
前端·数据库·echarts
测试老哥1 小时前
外包干了两年,技术退步明显。。。。
自动化测试·软件测试·python·功能测试·测试工具·面试·职场和发展
終不似少年遊*1 小时前
美国加州房价数据分析01
人工智能·python·机器学习·数据挖掘·数据分析·回归算法
如若1232 小时前
对文件内的文件名生成目录,方便查阅
java·前端·python
西猫雷婶2 小时前
python学opencv|读取图像(二十一)使用cv2.circle()绘制圆形进阶
开发语言·python·opencv