联通用户管理【从0到1实现一个Django项目】

联通用户管理

1.安装MySQL

1.1下载

  • MySQL压缩包

https://downloads.mysql.com/archives/community/

在这里插入图片描述

  • windows补丁: vcredist_explore

1.2安装

mysql-5.7.31-winx64.zip 是免安装的版本。

  • 解压zip文件
  • 将解压后的文件夹放入路径(不要有中文路径)

在这里插入图片描述

1.3创建配置文件

创建一个txt文件,写入以下内容,最后将文件后缀改为.ini

在这里插入图片描述

1.4初始化

  • 打开终端, 以管理员的权限去运行

  • ​ 输入初始化的命令 (mysql-5.7.31-winx64\bin\mysqld.exe路径–initialize-insecure)

    "C:\Program Files\mysql-5.7.31-winx64\bin\mysqld.exe"  --initialize-insecure
    

    执行命令后 mysql-5.7.31-winx64文件夹里新增一个 data 目录

这里,MySQL已安装完成。

2.启动MySQL

将 C:\Program Files\mysql-5.7.31-winx64\bin\ 添加到系统环境变量

>>>mysql -u root -p

在这里插入图片描述
在这里插入图片描述

3.MySQL常用语句

3.1数据库管理

  • 连接
mysql -u root -p; 
  • 关闭连接(退出)
exit;
  • 查看数据库
show database; 
  • 创建数据库
create database 数据库名字 DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
  • 删除数据库
drop database 数据库名字;
  • 进入数据库
use 数据库名字;
  • 查看文件夹下所有的数据表
show tables;

3.2数据表管理

  • 新增数据
insert into 表名(列名,列名) values(值,值);
insert into 表名(列名,列名) values(值,值),(值,值),(值,值),(值,值);
  • 删除数据
delete from 表名;
delete from 表名 where 条件;
  • 修改数据
update 表名 set 列=值;
update 表名 set 列=值,列=值;
update 表名 set 列=值 where 条件;
  • 查询数据
select * from 表名称;
select 列名称,列名称 from 表名称;
select 列名称,列名称 from 表名称 where 条件;

4.新建项目

在这里插入图片描述

5.新建app

在pycharm终端输入:

python manage.py startapp app01
  • 注册app

在这里插入图片描述

6.设计部门表、用户表

在这里插入图片描述

在这里插入图片描述

在app01中的models.py

from  django.db import models



class Department(models.Model):
    '''# 部门表'''
    title=models.CharField(verbose_name="标题",max_length=32)


class UserInfo(models.Model):
    ''' #员工表'''
    name=models.CharField(verbose_name="姓名",max_length=16)
    password=models.CharField(verbose_name="密码",max_length=64)
    age=models.IntegerField(verbose_name="年龄")
    account=models.DecimalField(verbose_name="账户余额",max_digits=10,decimal_places=2,default=0)
    create_time=models.DateTimeField(verbose_name="入职时间")

    #设外码,设级联删除
    depart=models.ForeignKey(to="Department",to_field="id",on_delete=models.CASCADE)

    gender_choices=(
        (1,'男'),
        (2,'女')
    )
    gender=models.SmallIntegerField(verbose_name="性别",choices=gender_choices)

7.在MySQL中生成表

  • 工具连接MySQL生成数据库。
create database User_management DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
  • 修改配置文件,连接MySQL
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'gx_day16',  # 数据库名字
        'USER': 'root',
        'PASSWORD': '',#我设置为空
        'HOST': '127.0.0.1',  # 那台机器安装了MySQL
        'PORT': 3306,
    }
}

在这里插入图片描述

django命令生成数据库表

在pycharm终端输入:

python manage.py makemigrations
python manage.py migrate

查看表结构:(表结构创建成功)

desc 表名;

在这里插入图片描述

8.静态文件管理

在app01目录下创一个static目录

  • 在static目录创建css、js、img、plugins目录

在app01目录下创一个templates目录(存放html文件

9.模板的继承

定义目版:layout.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="{% static 'plugin...min.css' %}">
    {% block css %}{% endblock %}
</head>
<body>
    <h1>标题</h1>
    <div>
        {% block content %}{% endblock %}
    </div>
    <h1>底部</h1>
    
    <script src="{% static 'js/jquery-3.6.0.min.js' %}"></script>
    {% block js %}{% endblock %}
</body>
</html>

继承母版:

{% extends 'layout.html' %}

{% block css %}
	<link rel="stylesheet" href="{% static 'pluxxx.css' %}">
	<style>
		...
	</style>
{% endblock %}


{% block content %}
    <h1>首页</h1>
{% endblock %}


{% block js %}
	<script src="{% static 'js/jqxxxin.js' %}"></script>
{% endblock %}

实现layout.html:

  • 设置导航条
  • 面板区域
  • 部门表格区域

9.1设计导航条

(1)引入静态文件

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.min.css' %}">
</head>
<body>

<script src="{% static 'js/jquery-3.6.0.min.js' %}"></script>
<script src="{% static 'plugins/bootstrap-3.4.1/js/bootstrap.min.js' %}"></script>
</body>
</html>

(2)在Bootstrap官网组件导航条示例代码,并修改

在这里插入图片描述

由于该示例为圆角效果,若要去除,只需在html文件中加:

<style>
        .navbar{
            border-radius: 0;
        }
    </style>

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.min.css' %}">
    
    <!--去除圆角效果--->
    <style>
        .navbar{
            border-radius: 0;
        }
    </style>
    
</head>
<body>
	<nav class="navbar navbar-expand-lg navbar-light bg-light">
        <div class="container-fluid">
            <a class="navbar-brand" href="#">Navbar</a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarSupportedContent">
                <ul class="navbar-nav me-auto mb-2 mb-lg-0">
                  <li class="nav-item">
                    <a class="nav-link active" aria-current="page" href="#">Home</a>
                  </li>
                  <li class="nav-item">
                    <a class="nav-link" href="#">Link</a>
                  </li>
                  <li class="nav-item dropdown">
                    <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
                      Dropdown
                    </a>
                    <ul class="dropdown-menu" aria-labelledby="navbarDropdown">
                      <li><a class="dropdown-item" href="#">Action</a></li>
                      <li><a class="dropdown-item" href="#">Another action</a></li>
                      <li><hr class="dropdown-divider"></li>
                      <li><a class="dropdown-item" href="#">Something else here</a></li>
                    </ul>
                  </li>
                  <li class="nav-item">
                    <a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
                  </li>
                </ul>
                <form class="d-flex">
                  <input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
                  <button class="btn btn-outline-success" type="submit">Search</button>
                </form>
            </div>
        </div>
    </nav>
<script src="{% static 'js/jquery-3.6.0.min.js' %}"></script>
<script src="{% static 'plugins/bootstrap-3.4.1/js/bootstrap.min.js' %}"></script>
</body>
</html>

启动Django项目

若要看渲染效果,首先需要启动Django项目,并设置urls文件中的urlpatterns:

python manage.py runserver

**注意:**单打开HTML是没有效果的

(3)删除组件中我们不需要的部分:

  • “Dropdown”下拉菜单
  • “Search”输入框
  • “Submit”提交键
  • 最右边的“Dropdown”下拉菜单(作为)

在这里插入图片描述

最后,layout.html:

<nav class="navbar navbar-default">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapse"
                    data-toggle="collapse" data-target="#bs-example-navbar-collaspe-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>
            <a class="navbar-brand" href="/user/list">用户管理</a>
            <a class="navbar-brand" href="/depart/list">部门管理</a>
            <a class="navbar-brand" href="/pretty/list">靓号管理</a>
        </div>

        <div class="collapse navbar-collaspe" 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="#">Link</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">云浮市</a>
                    <ul class="dropdown-menu">
                        <li><a href="#">个人资料</a> </li>
                        <li><a href="#">我的信息</a> </li>
                        <li role="separator" class="divider"> </li>
                        <li><a href="#">注销</a> </li>
                     </ul>
                </li>

            </ul>

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

9.2面板区域

(模板的继承)

完成导航栏后,在html中导航栏部分的下面添加:

<div>
    <div class="container">
        <div>
            <a class="btn btn-primary" href="depart_add.html">新建部门</a>
            {#新建一个 depart_add.html , 用来写新建部门界面#}
        </div>
</div>

(1)在Bootstrap官网组件带表格面板示例代码,并修改:

在这里插入图片描述

<div>
    <div class="container">
        <div>
            <a class="btn btn-primary" href="depart_add.html">新建部门</a>
            {#新建一个 depart_add.html , 用来写新建部门界面#}
        </div>
        <div class="panel panel-default">
            <div class="panel-heading">Panel heading without title</div>
            <div class="panel-body">
                Panel content
            </div>
        </div>
            
		{#然后引入表格}
        <table class="table">
           ...   
        </table>
            
</div>

(2)部门列表的代码示例:

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.min.css' %}">
    <style>
        .navbar {
            border-radius: 0;
        }
    </style>
</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="#">部门管理</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">Coder01 <span class="caret"></span></a>
                    <ul class="dropdown-menu">
                        <li><a href="#">个人资料</a></li>
                        <li><a href="#">我的信息</a></li>
                        <li role="separator" class="divider"></li>
                        <li><a href="#">注销</a></li>
                    </ul>
                </li>
            </ul>
        </div>
    </div>
</nav>

<div>
    <div class="container">
        <div style="margin-bottom: 10px">
            <a class="btn btn-success" href="depart_add.html">
                <span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>
                新建部门
            </a>
            {#新建一个 depart_add.html , 用来写新建部门界面#}
        </div>
        <div class="panel panel-default">
            <!-- Default panel contents -->
            <div class="panel-heading">
                <span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>
                部门列表
            </div>

            <!-- 表格部分 -->
            <table class="table table-bordered">
                <thead>
                <tr>
                    <th>ID</th>
                    <th>名称</th>
                    <th>操作</th>
                </tr>
                </thead>
                <tbody>
                <tr>
                    <th>1</th>
                    <td>销售部</td>
                    <td>
                        <a class="btn btn-primary btn-xs">编辑</a>
                        {# btn ==> 添加btn的按钮#}
                        {# btn-primary ==> 蓝色按钮(按钮的样式)#}
                        {# btn-xs ==> 	这会让按钮看起来特别小(按钮大小)#}
                        <a class="btn btn-danger btn-xs">删除</a>
                    </td>
                </tr>
                </tbody>
            </table>
        </div>
    </div>

</div>

<script src="{% static 'js/jquery-3.6.0.min.js' %}"></script>
<script src="{% static 'plugins/bootstrap-3.4.1/js/bootstrap.min.js' %}"></script>
</body>
</html>

10.部门管理

在这里插入图片描述

  • 部门列表
  • 添加部门
  • 删除部门
  • 编辑部门

10.1部门列表

(1)向表Depart添加数据,方便观察depart_list.html的渲染效果

mysql -u root -p
use User_management;
show tables;
desc app01_department;

insert into app01_department(title) values("游戏部"),("销售部");
select * from app01_department;

(2)在后端获取数据,并传入前端

app01/views.py

from django.shortcuts import render,redirect
from app01 import models


# Create your views here.
def depart_list(request):
    """部门列表"""

    # 去数据库中获取所有的部门列表
    # 通过导入模块的方式直接获取所有表,存入queryset,省的表多的时候一个一个导入太麻烦了
    queryset = models.Department.objects.all()  # queryset 形式就是一个一个对象 [对象,对象,对象]

    return render(request, "depart_list.html", {'queryset': queryset})


(3)前端处理,新建depart_list.html 继承 layout.html

depart_list.html代码:

<head>
    <meta charset="utf-8">
</head>
{% extends 'layout.html' %}

{% block content %}
    <div class="container">
        <div style="margin-bottom: 10px">
            <a class="btn btn-success" href="/depart/add">
                <span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>
                新建部门
            </a>
        </div>

        <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>ID</th>
                    <th>名称</th>
                    <th>操作</th>
                </tr>
                </thead>
                <tbody>
                {% for obj in queryset %}  {#后端获取的数据,传到前端进行渲染#}
                    <tr>
                        <th>{{ obj.id }}</th>
                        <td>{{ obj.title }}</td>
                        <td>
                            <a class="btn btn-primary btn-xs" href="/depart/{{ obj.id }}/edit/">编辑</a>
                            <a class="btn btn-danger btn-xs" href="/depart/delete/?nid={{ obj.id }}">删除</a>
                        </td>
                    </tr>
                {% endfor %}
                </tbody>
            </table>
        </div>
    </div>
{% endblock %}

(4)设置url

联通用户管理/urls.py中修改urlpatterns

#导入views
from app01 import views

urlpatterns = [
    # path('admin/', admin.site.urls),
    #部门管理
    path('depart/list',views.depart_list),

]

部门列表效果:

在这里插入图片描述

10.2添加部门

(1)前端:新建depart_add.html(同样继承layout.html)

同样在Bootstrap官网组件带表格面板示例代码,将表格部分代码,改为表单:

<head>
    <meta charset="UTF-8">
</head>

{% extends 'layout.html' %}
{% block content %}
    <div class="container">
        <div class="panel panel-default">
            <div class="panel-heading">
                <h3 class="panel-title">新建部门</h3>
            </div>
            <div class="panel-body">
                <!--表单-->
                <form method="post">
                    {% csrf_token %}
                    {#      上面两行代码处理post请求,得到用户输入的数据           #}
                    <div class="form-group">
                        <label>标题</label>
                        <input type="text" class="form-control" placeholder="标题" name="title">
                    </div>
                    <button type="submit" class="btn btn-primary">提交</button>
                </form>
            </div>
        </div>
    </div>
{% endblock %}

注意,在每个输入框( 设置一个name属性,后面在后端获取输入的数据)

(2)设置相关路径

  • 设置depart_list.html中新建部门按钮的跳转 href=“/depart/add”

在这里插入图片描述

(3)后端处理

app01/views.py

def depart_add(request):
    '''添加部门'''
    #1.处理GET请求
    #在部门聊天界面点击’新建部门‘-----GET请求(去deaprt_add.html)
    if request.method=='GET':
        return render(request,'depart_add.html')

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

    #3. 保存到数据库
    models.Department.objects.create(title=title)
    #4. 重定向回部门列表
    return  redirect('/depart/list')

(4)设置url

联通用户管理/urls.py中修改urlpatterns

#导入views
from app01 import views

urlpatterns = [
    # path('admin/', admin.site.urls),
    #部门管理
    path('depart/list',views.depart_list),
    path('depart/add/',views.depart_add),

]

点击‘新建部门’的效果:

在这里插入图片描述

10.3删除部门

(1)设置删除按钮的跳转路径

  • 设置depart_list.html中删除按钮的跳转 href=“/depart/delete?nid={obj.id}”

在这里插入图片描述

(2)因为删除部门直接点击‘删除’按钮即可,不需要前端渲染,所以只需后端处理

app01/views.py

def depart_detele(request):
    '''删除部门'''
    #获取ID
    # 'https//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')


(3)设置url

联通用户管理/urls.py中修改urlpatterns

#导入views
from app01 import views

urlpatterns = [
    # path('admin/', admin.site.urls),
    #部门管理
    path('depart/list',views.depart_list),
    path('depart/add/',views.depart_add),
    path('depart/delete/',views.depart_delete),

]

10.4编辑部门

(1)设置编辑按钮的跳转路径

  • 设置depart_list.html中编辑按钮的跳转 href=“/depart/{{obj.id}}/edit”

在这里插入图片描述

(2)点击 “编辑按钮” 后端处理:

def depart_edit(request,nid):
    '''修改部门'''
    if request.method=='GET':
        # 根据nid,获得他的数据
        row_object = models.Department.objects.filter(id=nid).first()
        print(row_object.id,row_object.title)

        return render(request, 'depart_edit.html', {'row_object': row_object})

(3)前端渲染

新建depart_edit.html,从后端获取数据并渲染

<head>
    <meta charset="UTF-8">
</head>

{% extends 'layout.html' %}
{% block content %}
    <div class="container">
        <div class="panel panel-default">
            <div class="panel-heading">
                <h3 class="panel-title">编辑部门</h3>
            </div>
            <div class="panel-body">
                <form method="post">
                    {% csrf_token %}
                    {#      上面两行代码处理post请求,得到用户输入的数据           #}
                    <div class="form-group">
                        <label>标题</label>
                        <input type="text" class="form-control" placeholder="标题" value="{{ row_object.title }}" name="title">
                    </div>
                    <button type="submit" class="btn btn-primary">修改</button>
                </form>
            </div>
        </div>
    </div>
{% endblock %}

(4)点击”修改“,后端处理:

def depart_edit(request,nid):
    '''修改部门'''
    if request.method=='GET':
        # 根据nid,获得他的数据
        row_object = models.Department.objects.filter(id=nid).first()
        print(row_object.id,row_object.title)

        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/")

(5)设置url

联通用户管理/urls.py中修改urlpatterns

#导入views
from app01 import views

urlpatterns = [
    # path('admin/', admin.site.urls),
    #部门管理
    path('depart/list',views.depart_list),
    path('depart/add/',views.depart_add),
    path('depart/delete/',views.depart_delete),
    path('depart/<int:nid>/edit/',views.depart_edit),

]

至此部门管理这部分已经完成,接下来的用户管理、靓号管理大部分都差不多

11.用户管理

  • 用户列表
  • 添加用户
  • 删除用户
  • 编辑用户

11.1用户列表

(1)向表Userinfo添加数据,方便观察user_list.html的渲染效果

insert into app01_userinfo(name,password,age,account,create_time,gender,depart_id) values("韩超","666",23,100.68,"2020-01-11",2,1);

insert into app01_userinfo(name,password,age,account,create_time,gender,depart_id) values("刘东","123",23,100.68,"2010-11-11",1,4);

insert into app01_userinfo(name,password,age,account,create_time,gender,depart_id) values("朱虎飞","999",33,9900.68,"2021-05-11",1,1);

(2)在后端获取数据,并传入前端

app01/views.py

def user_list(request):
    '''用户管理'''
    # 获取所有用户列表 [obj,obj,obj]
    queryset=models.UserInfo.objects.all()
    '''
    # 用Python的语法获取数据
    for obj in queryset:
        print(obj.id, obj.name, obj.account, obj.create_time.strftime("%Y-%m-%d"), obj.gender, obj.get_gender_display(),
              obj.depart_id, obj.depart.title)
        # print(obj.name, obj.depart_id)
        # obj.depart_id  # 获取数据库中存储的那个字段值
        # obj.depart.title  # 根据id自动去关联的表中获取哪一行数据depart对象。
    '''
    return render(request,'user_list.html',{'queryset':queryset})

(3)前端处理,新建user_list.html 继承 layout.html

user_list.html代码:

<head>
    <meta charset="utf-8">
</head>
{% extends 'layout.html' %}

{% block content %}
    <div class="container">
        <div style="margin-bottom: 10px">
            <a class="btn-success" href="/user/add/">
                <span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>
                新建用户
            </a>
            <a class="btn-success" href="/user/model/form/add/">
                <span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>
                新建用户ModelForm
            </a>
        </div>

        <div class="panel panel-default">
            <!-- Default panel contents -->
             <div class="panel-heading"><span class="glyphicon glyphicon-list" aria-hidden="true"></span> 用户列表</div>


            <!-- Table -->
            <table class="table">
                <thead>
                    <tr>
                        <th>ID</th>
                        <th>姓名</th>
                        <th>密码</th>
                        <th>年龄</th>
                        <th>余额</th>
                        <th>入职时间</th>
                        <th>性别</th>
                        <th>所属部门</th>
                        <th>操作</th>
                    </tr>
                </thead>
                <tbody>
                {% for obj in queryset %}
                    <tr>
                        <th>{{ obj.id }}</th>
                        <td>{{ obj.name }}</td>
                        <td>{{ obj.password }}</td>
                        <td>{{ obj.age }}</td>
                        <td>{{ obj.account }}</td>
                        <td>{{ obj.create_time|date:"Y-m-d" }}</td>
                        <td>{{ obj.get_gender_display }}</td>
                        <td>{{ obj.depart.title }}</td>
                        <td>
                            <a class="btn btn-primary btn-xs" href="/user/{{ obj.id }}/edit" >编辑</a>
                            <a class="btn btn-danger btn-xs" href="/user/delete/?nid={{ obj.id }}" >删除</a>
                        </td>
                    </tr>
                {% endfor %}
                </tbody>
            </table>
        </div>


    </div>
{% endblock %}

(4)设置url

联通用户管理/urls.py中修改urlpatterns

urlpatterns = [
    # path('admin/', admin.site.urls),

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

    #用户
    path('user/list/',views.user_list),
]

用户列表效果:

在这里插入图片描述

11.2添加用户

(1)前端:新建user_add.html(同样继承layout.html)

{% extends 'layout.html' %}
{% load static %}

{% block content %}
    <div class="container">
        <div class="panel panel-default">
            <div class="panel-heading">
                <h3 class="panel-title">新建用户</h3>
            </div>
            <div class="panel-body">
                <form method="post">
                    {% csrf_token %}
                      <div class="form-group">
                            <label>姓名</label>
                            <input type="text" class="form-control" placeholder="姓名" name="user">
                      </div>
                      <div class="form-group">
                            <label>密码</label>
                            <input type="text" class="form-control" placeholder="密码" name="pwd">
                      </div>
                      <div class="form-group">
                            <label>年龄</label>
                            <input type="text" class="form-control" placeholder="年龄" name="age">
                      </div>
                      <div class="form-group">
                            <label>余额</label>
                            <input type="text" class="form-control" placeholder="余额" name="ac">
                      </div>
                      <div class="form-group">
                            <label>入职时间</label>
                            <input  id="dt" type="text" class="form-control" placeholder="入职时间" name="ctime">
                      </div>
                      <div class="form-group">
                            <label>性别</label>
                            <select class="form-control" name="gd">
                                {% for item in gender_choices %}
                                    <option value="{{ item.0 }}">{{ item.1 }}</option>
                                {% endfor %}
{#                                <option value="1"></option>#}
{#                                <option value="2"></option>#}
                            </select>
                      </div>
                      <div class="form-group">
                            <label>部门</label>
                            <select class="form-control" name="dp">
                                {% for item in depart_list %}
                                    <option value="{{ item.id }}">{{ item.title }}</option>
                                {% endfor %}
                            </select>
                      </div>

                      <button type="submit" class="btn btn-default">Submit</button>
                </form>
            </div>
        </div>
    </div>
{% endblock %}



注意,在每个输入框( 设置一个name属性,后面在后端获取输入的数据)

(2)设置相关路径

  • 设置user_list.html中新建用户按钮的跳转 href=“/user/add”

(3)后端处理

app01/views.py

def user_add(request):
    '''添加用户'''
    # 1.处理GET请求
    # 在部门聊天界面点击’新建部门‘-----GET请求(去user_add.html)
    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('user')
    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,create_time=ctime,gender=gender,depart_id=depart_id)#写为depart=depart_id,新建时会报错

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

(4)设置url

联通用户管理/urls.py中修改urlpatterns

urlpatterns = [
    # path('admin/', admin.site.urls),

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

    #用户
    path('user/list/',views.user_list),
    path('user/add/',views.user_add),
]

点击‘新建部门’的效果:

在这里插入图片描述

11.3删除用户

(1)设置删除按钮的跳转路径

  • 设置user_list.html中删除按钮的跳转 href=“/user/delete?nid={obj.id}”

(2)因为删除用户直接点击‘删除’按钮即可,不需要前端渲染,所以只需后端处理

app01/views.py

def user_delete(request):
    '''删除用户'''
    # 获取ID
    # 'https//127.0.0.1:8000/user/delete/?nid=1'
    nid=request.GET.get('nid')
    #删除
    models.UserInfo.objects.filter(id=nid).delete()
    # 重定向回用户列表
    return redirect('/user/list/')

(3)设置url

联通用户管理/urls.py中修改urlpatterns

urlpatterns = [
    # path('admin/', admin.site.urls),

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

    #用户
    path('user/list/',views.user_list),
    path('user/add/',views.user_add),
    path('user/delete/',views.user_delete),
]

11.4编辑用户

(1)设置编辑按钮的跳转路径

  • 设置depart_list.html中编辑按钮的跳转 href=“/user/{{obj.id}}/edit”

(2)点击 “编辑按钮” 后端处理:

def user_edit(request,nid):
    '''编辑用户'''

    if request.method=="GET":
        # 根据nid,获得他的数据
        row_object =models.UserInfo.objects.filter(id=nid).first()
        context={
            'depart_list':models.Department.objects.all(),
            'row_object': row_object
        }

        return render(request,'user_edit.html',context)

(3)前端渲染

新建user_edit.html,从后端获取数据并渲染

<head>
    <meta charset="UTF-8">
</head>

{% extends 'layout.html' %}
{% block content %}
    <div class="container">
        <div class="panel panel-default">
            <!-- Default panel contents -->
            <div class="panel-heading"><h3>编辑用户</h3></div>
            <div class="panel-body">
                <form method="post">
                    {% csrf_token %}
                    <span class="glyphicon glyphicon-user"></span><label >姓名</label>
                    <div class="form-group">
                      <input type="text" class="form-control" placeholder="{{ row_object.name }}" name="user" >
                    </div>

                    <span class="glyphicon glyphicon-user"></span><label >密码</label>
                    <div class="form-group">
                      <input type="text" class="form-control" placeholder="{{ row_object.password }}" name="pwd" >
                    </div>

                     <span class="glyphicon glyphicon-user"></span><label >年龄</label>
                    <div class="form-group">
                      <input type="text" class="form-control" placeholder="{{ row_object.age }}" name="age" >
                    </div>

                     <span class="glyphicon glyphicon-user"></span><label >余额</label>
                    <div class="form-group">
                      <input type="text" class="form-control" placeholder="{{ row_object.account }}" name="ac" >
                    </div>

                     <span class="glyphicon glyphicon-user"></span><label >入职时间</label>
                    <div class="form-group">
                      <input type="text" class="form-control" placeholder="{{ row_object.create_time|date:"Y-m-d"  }}" name="ctime" >
                    </div>

                     <span class="glyphicon glyphicon-user"></span><label >性别</label>
                    <div class="form-group">
                      <select class="form-control"  name="gd">
                        {% for item in row_object.gender_choices %}
                            <option value="{{ item.0 }}">{{ item.1 }}</option>
                        {% endfor %}
                    </select>
                    </div>

                     <span class="glyphicon glyphicon-user"></span><label >部门</label>
                    <div class="form-group">
                      <select class="form-control" name="dp">
                          {% for item in depart_list %}
                              <option value="{{ item.id }}">{{ item.title }}</option>
                          {% endfor %}
                      </select>
                    </div>

                    <button type="submit" class="btn btn-primary">修改</button>
                </form>
            </div>
        </div>
    </div>
{% endblock %}

(4)点击”修改“,后端处理:

def user_edit(request,nid):
    '''编辑用户'''

    if request.method=="GET":
        # 根据nid,获得他的数据
        row_object =models.UserInfo.objects.filter(id=nid).first()
        context={
            'depart_list':models.Department.objects.all(),
            'row_object': row_object
        }

        return render(request,'user_edit.html',context)

    #处理post
    # 获取用户提交的数据
    user=request.POST.get('user')
    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.filter(id=nid).update(name=user,password=pwd,age=age,account=account,create_time=ctime,gender=gender,depart=depart_id)

    return redirect('/user/list/')

(5)设置url

联通用户管理/urls.py中修改urlpatterns

#导入views
from app01 import views

urlpatterns = [
    # path('admin/', admin.site.urls),
    #部门管理
    path('depart/list',views.depart_list),
    path('depart/add/',views.depart_add),
    path('depart/delete/',views.depart_delete),
    path('depart/<int:nid>/edit/',views.depart_edit),

]

至此,用户管理部分已完成。

12.Django组件:Form和ModelForm

在部门管理、用户管理部分都是用原始的方式实现新建,例如“新建用户”

原始方式理思路:许多情况下会很麻烦

  • 用户提交数据没有校验。
  • 错误,页面上应该有错误提示(例:用户民未填写)。
  • 页面上,没一个字段都需要我们重新写一遍。数据多了简直爆炸
  • 关联的数据,手动去获取并展示循环展示在页面。

所以,我们采用Django组件:

  • Form组件(简便,可以解决前三个问题)
  • ModelForm组件(最简便)

12.1初识Form

我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来。

与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用户是否输入,输入的长度和格式等正不正确。如果用户输入的内容有错误就需要在页面上相应的位置显示对应的错误信息.。

Django form组件就实现了上面所述的功能。

总结一下,其实form组件的主要功能如下:

生成页面可用的HTML标签
对用户提交的数据进行校验
保留上次输入内容

1. views.py

class MyForm(Form):
    user = forms.CharField(widget=forms.Input)
    pwd = form.CharFiled(widget=forms.Input)
    email = form.CharFiled(widget=forms.Input)
    account = form.CharFiled(widget=forms.Input)
    create_time = form.CharFiled(widget=forms.Input)
    depart = form.CharFiled(widget=forms.Input)
    gender = form.CharFiled(widget=forms.Input)


def user_add(request):
    if request.method == "GET":
        form = MyForm()
        return render(request, 'user_add.html',{"form":form})

2.user_add.html

<form method="post">
    {% for field in form%}
    	{{ field }}
    {% endfor %}
    <!-- <input type="text"  placeholder="姓名" name="user" /> -->
</form>
<form method="post">
    {{ form.user }}
    {{ form.pwd }}
    {{ form.email }}
    <!-- <input type="text"  placeholder="姓名" name="user" /> -->
</form>

12.2 ModelForm(推荐)

0. models.py

class UserInfo(models.Model):
    """ 员工表 """
    name = models.CharField(verbose_name="姓名", max_length=16)
    password = models.CharField(verbose_name="密码", max_length=64)
    age = models.IntegerField(verbose_name="年龄")
    account = models.DecimalField(verbose_name="账户余额", max_digits=10, decimal_places=2, default=0)
    create_time = models.DateTimeField(verbose_name="入职时间")
    depart = models.ForeignKey(to="Department", to_field="id", on_delete=models.CASCADE)
    gender_choices = (
        (1, "男"),
        (2, "女"),
    )
    gender = models.SmallIntegerField(verbose_name="性别", choices=gender_choices)

1. views.py

class MyForm(ModelForm):
    xx = form.CharField*("...")
    class Meta:
        model = UserInfo
        fields = ["name","password","age","xx"]


def user_add(request):
    if request.method == "GET":
        form = MyForm()
        return render(request, 'user_add.html',{"form":form})

2.user_add.html

<form method="post">
    {% for field in form%}
    	{{ field }}
    {% endfor %}
    <!-- <input type="text"  placeholder="姓名" name="user" /> -->
</form>
<form method="post">
    {{ form.user }}
    {{ form.pwd }}
    {{ form.email }}
    <!-- <input type="text"  placeholder="姓名" name="user" /> -->
</form>

可以利用Django组件来修改前面的部门管理、用户管理

13.靓号管理

表结构:

在这里插入图片描述

根据表结构的需求,在models.py中创建类(由类生成数据库中的表)。

class PrettyNum(models.Model):
    """ 靓号表 """
    mobile = models.CharField(verbose_name="手机号", max_length=11)
    # 想要允许为空 null=True, blank=True
    price = models.IntegerField(verbose_name="价格", default=0)

    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)

django命令生成数据库表

在pycharm终端输入:

python manage.py makemigrations
python manage.py migrate

向靓号表添加数据:

insert into app01_prettynum(mobile,price,level,status)values("111111111",19,1,1);

13.1靓号列表

  • URL

  • 函数

    • 获取所有的靓号

    • 结合html+render将靓号罗列出来

      id	号码	价格	级别(中文)	状态(中文)
      
  • urls.py
urlpatterns = [
    # path('admin/', admin.site.urls),

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

    #用户
    path('user/list/',views.user_list),
    path('user/add/',views.user_add),
    path('user/delete/',views.user_delete),
    path('user/<int:nid>/edit/',views.user_edit),
    
    #靓号管理
    path('pretty/list/',pretty.pretty_list),
]
  • views.py
def pretty_list(request):
    '''靓号管理'''
    # 获取所有靓号列表 [obj,obj,obj]
    queryset=models.PrettyNum.objects.all()
    return render(request,'pretty_list.html',{'queryset':queryset})
  • pretty_list.html (跟user_list类似)
<head>
    <meta charset="utf-8">
</head>
{% extends 'layout.html' %}

{% block content %}
<div class="container">
    <div style="margin-bottom: 10px">
        <a class="btn btn-success" href="/pretty/add/">
            <span class="glyphicon glyphicon-plus-sign"></span>
            新建靓号
        </a>
    </div>
    <div class="panel panel-default">
      <!-- Default panel contents -->
      <div class="panel-heading">
          <h3><span class="glyphicon glyphicon-list"></span> 靓号管理</h3>
      </div>

      <!-- Table -->
      <table class="table">
        <thead>
            <tr>
                <th>ID</th>
                <th>号码</th>
                <th>价格</th>
                <th>级别</th>
                <th>状态</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
            {% for obj in queryset %}
             <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-primary btn-xs" href="/pretty/{{ obj.id }}/edit">编辑</a>
                    <a class="btn-danger btn-xs" href="/pretty/delete/?nid={{ obj.id }}">删除</a>
                </td>
             </tr>
            {% endfor %}
        </tbody>
      </table>
    </div>

{#    分页#}
    <div class="clearfix">
        <ul class="pagination">
            {{ page_string }}
        </ul>
    </div>
</div>
{% endblock %}
  • layout.html

在导航条添加【部门管理】【用户管理】【靓号管理】,设置对应的herf(这部分在一开始的layout.html已经添加)

<ul class="nav navbar-nav">
                <li><a href="/depart/list/">部门管理</a></li>
                <li><a href="/user/list/">用户管理</a></li>
                <li><a href="/mobile/list/">靓号管理</a></li>
            </ul>

13.2新建靓号(ModelForm组件)

该部分不用原始方式,利用Django的ModelForm组件,与原始方式对比

1).models.py

class PrettyNum(models.Model):
    '''靓号表'''
    mobile=models.CharField(verbose_name='手机号',max_length=11,null=True)
    # 想要允许为空 null=True, blank=True

    price=models.IntegerField(verbose_name='价格',default=0)

    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)views.py

from django.forms import ModelForm

"""新建靓号(ModelForm版本)"""


class MyForm(ModelForm):
    # xx = form.CharField*("...")  # ModelForm不仅支持数据库中支持的字段,我们也可以另外自定义
    class Meta:
        model = models.PrettyNum
        # fields = ["name", "password", "age", "xx"]
        fields = ["mobile", "price", "level", 'status']

        # 我们可以使用下面的方法去获取各个属性,但有个更方便的方法:重定义_init__
        # widgets = {
        #     "mobile": forms.TextInput(attrs={"class": "form-control"}),
        #     "price": forms.PasswordInput(attrs={"class": "form-control"}),
        #     "level": forms.TextInput(attrs={"class": "form-control"}),
        #     "status": forms.TextInput(attrs={"class": "form-control"}),
        # }

    # 重定义_init__
    # 在前端中,我们使用ModelForm的组件中,前端失去了 class="form-control" 效果,我们可以通过Django控制插件
    # 循环找到所有的插件,添加了class="form-control"
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for name, field in self.fields.items():
            # 如果你不想要某一个属性不使用 class="form-control" 这个效果,如 : password
            # if name == "password":
            #     continue
            field.widget.attrs = {"class": "form-control", "placeholder": field.label}


def pretty_add(request):
    form = MyForm()
    return render(request, 'pretty_add.html', {"form": form})

3).pretty_add.html

<head>
    <meta charset="utf-8">
</head>

{% extends 'layout.html' %}

{% block content %}
<div class="container">
    <div class="panel panel-default">
      <!-- Default panel contents -->
      <div class="panel-heading">
          <h3 class="panel-title">新建靓号</h3>
      </div>
      <div class="panel-body">
        <form method="post" novalidate="novalidate">
              {% csrf_token %}
              {% for field in form %}
                  <div class="form-group">
                    <label>{{ field.label }}</label>
                    {{ field }}
                    <span style="color:#ff0000;">{{ field.errors.0 }}</span>
                  </div>
              {% endfor %}
              <button type="submit" class="btn btn-success">提交</button>
        </form>
      </div>


    </div>
</div>
{% endblock %}

4)urls.py

urlpatterns = [
    # path('admin/', admin.site.urls),

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

    #用户
    path('user/list/',views.user_list),
    path('user/add/',views.user_add),
    path('user/delete/',views.user_delete),
    path('user/<int:nid>/edit/',views.user_edit),

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

5)将数据储存至数据库,views.py:

def pretty_add(request):
    if request.method=='GET':
        form = MyForm()
        return render(request, 'pretty_add.html', {"form": form})

    # 用户POST提交数据,数据效验(不能为空)
    # 如果数据合法,提交到数据库
    form = MyForm(data=request.POST)
    if form.is_valid():
        print(form.cleaned_data)
        # 将数据存储至数据库
        # 方法一:老套路
        # models.PrettyNum.objects.create(...)
        # 方法二:ModelForm
        form.save()
        return redirect("/pretty/list/")
    # else:
    #     print(form.errors)

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


效果:

  • 用户POST提交数据,数据效验(不能为空)
  • 如果数据合法,提交到数据库

在这里插入图片描述

13.3删除靓号

  • pretty_list.html

     <td>
                        <a class="btn-primary btn-xs" href="/pretty/{{ obj.id }}/edit">编辑</a>
                        <a class="btn-danger btn-xs" href="/pretty/delete/?nid={{ obj.id }}">删除</a>
                    </td>
    
  • urls.py

path('pretty/delete/',views.pretty_delete),
  • views.py
def pretty_delete(request):
    '''删除靓号'''
    # 获取ID
    nid=request.GET.get('nid')
    models.PrettyNum.objects.filter(id=nid).delete()
    return redirect('/pretty/list/')

13.4 编辑靓号

  • pretty_list.html

     <td>
                        <a class="btn-primary btn-xs" href="/pretty/{{ obj.id }}/edit">编辑</a>
                        <a class="btn-danger btn-xs" href="/pretty/delete/?nid={{ obj.id }}">删除</a>
                    </td>
    
  • urls.py

path('pretty/<int:nid>/edit/',views.pretty_edit),
  • views.py
def pretty_edit(request,nid):
    '''编辑靓号'''
    row_object = models.PrettyNum.objects.filter(id=nid).first()
    if request.method=='GET':
        context={
            'row_object':row_object,
            'form':MyForm()
        }
        return render(request, 'pretty_edit.html',context)
  • pretty_edit.html
<head>
    <meta charset="utf-8">
</head>

{% extends 'layout.html' %}

{% block content %}
<div class="container">
    <div class="panel panel-default">
      <div class="panel-heading">
        <h3 class="panel-title">编辑靓号</h3>
      </div>
      <div class="panel-body">
        <form method="post">
        {% csrf_token %}
{#         传统方法    #}
{#            <div class="form-group">#}
{#                <label>手机号</label>#}
{#                <input type="text" class="form-control" placeholder="{{ row_object.mobile  }}" name="mobile">#}
{#            </div>#}
{##}
{#            <div class="form-group">#}
{#                <label>价格</label>#}
{#                <input type="text" class="form-control" placeholder="{{ row_object.price  }}" name="price">#}
{#            </div>#}
{##}
{#            <div class="form-group">#}
{#                <label>级别</label>#}
{#                <select class="form-group" name="level">#}
{#                    {% for item in row_object.level_choices %}#}
{#                        <option value="{{ item.0 }}">{{ item.1 }}</option>#}
{#                    {% endfor %}#}
{#                </select>#}
{#            </div>#}
{##}
{#            <div class="form-group">#}
{#                <label>状态</label>#}
{#                <select class="form-group" name="level">#}
{#                    {% for item in row_object.status_choices %}#}
{#                        <option value="{{ item.0 }}">{{ item.1 }}</option>#}
{#                    {% endfor %}#}
{#                </select>#}
{#            </div>#}
{##}
{#            <button type="submit" class="btn btn-primary">修改</button>#}
            {% for field in form %}
                <div class="form-group">
                    <label>{{ field.label }}</label>
                    {{ field }}
                    <span style="color:red;">{{ field.errors.0 }}</span>
                </div>
            {% endfor %}
            <button type="submit" class="btn btn-primary">修改</button>
        </form>
      </div>
    </div>
</div>
{% endblock %}
  • views.py
def pretty_edit(request,nid):
    '''编辑靓号'''
    row_object = models.PrettyNum.objects.filter(id=nid).first()
    if request.method=='GET':
        context={
            'row_object':row_object,
            'form':MyForm()
        }
        return render(request, 'pretty_edit.html',context)


    form=MyForm(data=request.POST)
    if form.is_valid:
        form.save()
        return redirect('/pretty/list/')

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

13.5优化

13.5.1 验证方式1:

字段+正则表达式

通过正则表达式,限制输入格式

  • views.py

    from django.forms import ModelForm
    
    """新建靓号(ModelForm版本)"""
    from django.core.validators import RegexValidator
    from django import forms
    class MyForm(ModelForm):
        # xx = form.CharField*("...")  # ModelForm不仅支持数据库中支持的字段,我们也可以另外自定义
    
        ''''验证1'''
        '''
            【正则表达式】分析如下:
    
            手机号都为11位,所以必须限定匹配的数字的位数,通过$来限定以9位数字结尾,
    
            又因为手机号都以1开头,所以通过^1限定以1开头,
    
            然后手机号第二位貌似只有3,5,6,7,8,这几个数字,所以通过[3,5,6,7,8]来匹配其中的任一数字,
    
            最后{9}匹配9个/d。
        '''
        mobile = forms.CharField(
            label="手机号",
            validators=[RegexValidator(r'^1[3-9]\d{9}', "手机号格式错误"), ],  # 通过正则表达式限制输入格式,1开头,然后3~9,然后再来就个数
        # 当然这个正则表达式可以叠加,多重限制
        )
    
    
        class Meta:
            model = models.PrettyNum
            # fields = ["name", "password", "age", "xx"]
            fields = ["mobile", "price", "level", 'status']
    
            # 我们可以使用下面的方法去获取各个属性,但有个更方便的方法:重定义_init__
            # widgets = {
            #     "mobile": forms.TextInput(attrs={"class": "form-control"}),
            #     "price": forms.PasswordInput(attrs={"class": "form-control"}),
            #     "level": forms.TextInput(attrs={"class": "form-control"}),
            #     "status": forms.TextInput(attrs={"class": "form-control"}),
            # }
    
        # 重定义_init__
        # 在前端中,我们使用ModelForm的组件中,前端失去了 class="form-control" 效果,我们可以通过Django控制插件
        # 循环找到所有的插件,添加了class="form-control"
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            for name, field in self.fields.items():
                # 如果你不想要某一个属性不使用 class="form-control" 这个效果,如 : password
                # if name == "password":
                #     continue
                field.widget.attrs = {"class": "form-control", "placeholder": field.label}
    
    

    效果:

    在这里插入图片描述

13.5.2 验证方式2:

不允许手机号重复。

  • 添加:【正则表达式】【手机号不能存在】

    # [obj,obj,obj]
    queryset = models.PrettyNum.objects.filter(mobile="1888888888")
    
    obj = models.PrettyNum.objects.filter(mobile="1888888888").first()
    
    # True/False
    exists = models.PrettyNum.objects.filter(mobile="1888888888").exists()
    
  • 编辑:【正则表达式】【手机号不能存在】

    排除自己以外,其他的数据是否手机号是否重复?
    
    # id!=2 and mobile='1888888888'
    models.PrettyNum.objects.filter(mobile="1888888888").exclude(id=2)
    
  • # 验证:方式2
        def clean_mobile(self):
            txt_mobile = self.cleaned_data["mobile"]
    
            if len(txt_mobile) != 11:
                # 验证不通过
                raise ValidationError("格式错误")
    
            exists = models.PrettyNum.objects.filter(mobile=txt_mobile).exists()
            if exists:
                raise ValidationError("手机号已存在")
    
            # 验证通过,用户输入的值返回
            return txt_mobile
    

整个MyForm:

from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
from django import forms


class PrettyAddModelForm(ModelForm):
    # xx = form.CharField*("...")  # ModelForm不仅支持数据库中支持的字段,我们也可以另外自定义

    # ''''验证1'''
    # '''
    #     【正则表达式】分析如下:
    #
    #     手机号都为11位,所以必须限定匹配的数字的位数,通过$来限定以9位数字结尾,
    #
    #     又因为手机号都以1开头,所以通过^1限定以1开头,
    #
    #     然后手机号第二位貌似只有3,5,6,7,8,这几个数字,所以通过[3,5,6,7,8]来匹配其中的任一数字,
    #
    #     最后{9}匹配9个/d。
    # '''
    # mobile = forms.CharField(
    #     label='手机号',
    #     # 创建手机号的正则校验器
    #     validators=[RegexValidator(r'^1[3-9]\d{9}$', '手机号码错误'), ],
    #
    # )

    class Meta:
        model = models.PrettyNum
        # fields = ["name", "password", "age", "xx"]
        fields = ["mobile", "price", "level", 'status']

        # 我们可以使用下面的方法去获取各个属性,但有个更方便的方法:重定义_init__
        # widgets = {
        #     "mobile": forms.TextInput(attrs={"class": "form-control"}),
        #     "price": forms.PasswordInput(attrs={"class": "form-control"}),
        #     "level": forms.TextInput(attrs={"class": "form-control"}),
        #     "status": forms.TextInput(attrs={"class": "form-control"}),
        # }

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

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

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

        # 验证通过,用户输入的值返回
        return txt_mobile
    # 验证:方式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 clean_mobile(self):
    #     # 验证手机号是否已经存在
    #     mobile = self.data.get('mobile')
    #     exists = models.PrettyNum.objects.filter(mobile=mobile)
    #
    #     if exists:
    #         raise ValidationError("手机号已存在")
    #     # 验证通过,用户输入的值返回
    #     return mobile


    # 重定义_init__
    # 在前端中,我们使用ModelForm的组件中,前端失去了 class="form-control" 效果,我们可以通过Django控制插件
    # 循环找到所有的插件,添加了class="form-control"
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for name, field in self.fields.items():
            # 如果你不想要某一个属性不使用 class="form-control" 这个效果,如 : password
            # if name == "password":
            #     continue
            field.widget.attrs = {"class": "form-control", "placeholder": field.label}

效果:

在这里插入图片描述

编辑靓号用同样的验证方法:

class PrettyEditModelForm(ModelForm):
    '''
        【正则表达式】分析如下:

        手机号都为11位,所以必须限定匹配的数字的位数,通过$来限定以9位数字结尾,

        又因为手机号都以1开头,所以通过^1限定以1开头,

        然后手机号第二位貌似只有3,5,6,7,8,这几个数字,所以通过[3,5,6,7,8]来匹配其中的任一数字,

        最后{9}匹配9个/d。
    '''
    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)
        mobile=self.cleaned_data['mobile']
        exists=models.PrettyNum.objects.filter(mobile=mobile).exists()
        if exists:
            raise ValidationError("手机号已存在")
        # 验证通过,用户输入的值返回
        return mobile

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for name, field in self.fields.items():
            # 如果你不想要某一个属性不使用 class="form-control" 这个效果,如 : password
            # if name == "password":
            #     continue
            field.widget.attrs = {"class": "form-control", "placeholder": field.label}

在这里插入图片描述

然后,给编辑靓号也采用ModelForm:

  • pretty_edit.html

在这里插入图片描述

在表单加上属性:novalidate=“novalidate”

  • views.py

(与新建靓号一样)

class PrettyEditModelForm(ModelForm):
    print("FORM********************************")
    class Meta:
        model = models.PrettyNum
        fields = ['mobile','price','level','status']

        # 验证:方式2

    def clean_mobile(self):
        txt_mobile = self.cleaned_data["mobile"]

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

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

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

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for name, field in self.fields.items():
            # 如果你不想要某一个属性不使用 class="form-control" 这个效果,如 : password
            # if name == "password":
            #     continue
            field.widget.attrs = {"class": "form-control", "placeholder": field.label}

            
            
            
def pretty_edit(request, nid):
    '''编辑靓号'''
    row_object = models.PrettyNum.objects.filter(id=nid).first()
    if request.method == 'GET':
        context = {
            'row_object': row_object,
            'form': PrettyAddModelForm(instance=row_object)
        }
        return render(request, 'pretty_edit.html', context)

    form = PrettyAddModelForm(data=request.POST,instance=row_object)

    if form.is_valid():
        form.save()
        return redirect('/pretty/list/')


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

13.6搜索靓号

常用搜索方式:

models.PrettyNum.objects.filter(mobile="19999999991",id=12)

data_dict = {"mobile":"19999999991","id":123}
models.PrettyNum.objects.filter(**data_dict)

对于一些变量的控制:

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)  # 小于等于12

data_dict = {"id__lte":12}
models.PrettyNum.objects.filter(**data_dict)
models.PrettyNum.objects.filter(mobile="999")               # 等于
models.PrettyNum.objects.filter(mobile__startswith="1999")  # 筛选出以1999开头
models.PrettyNum.objects.filter(mobile__endswith="999")     # 筛选出以999结尾
models.PrettyNum.objects.filter(mobile__contains="999")     # 筛选出包含999

data_dict = {"mobile__contains":"999"}
models.PrettyNum.objects.filter(**data_dict)
<head>
    <meta charset="utf-8">
</head>
{% extends 'layout.html' %}

{% block content %}
<div class="container">
    <div style="margin-bottom: 10px" >
        <a class="btn btn-success" href="/pretty/add/">
            <span class="glyphicon glyphicon-plus-sign"></span>
            新建靓号
        </a>


{#             搜索靓号      #}
        <div style="float: right;width: 400px">
            <form method="post">
                {% csrf_token %}
                <div class="col-lg-6" >
                 <div class="input-group"  >
                      <input type="text"  class="form-control" placeholder="Search for..." name="mobile_search" value="{{ search_result }}">
                      <span class="input-group-btn" >
                          <button class="btn btn-default" type="submit" type="button">搜索</button>
                      </span>
                 </div><!-- /input-group -->
              </div><!-- /.col-lg-6 -->
            </form>
        </div>

    </div>

    <div class="panel panel-default">
      <!-- Default panel contents -->
      <div class="panel-heading">
          <h3><span class="glyphicon glyphicon-list"></span> 靓号管理</h3>
      </div>

      <!-- Table -->
      <table class="table">
        <thead>
            <tr>
                <th>ID</th>
                <th>号码</th>
                <th>价格</th>
                <th>级别</th>
                <th>状态</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
            {% for obj in queryset %}
             <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-primary btn-xs" href="/pretty/{{ obj.id }}/edit">编辑</a>
                    <a class="btn-danger btn-xs" href="/pretty/delete/?nid={{ obj.id }}">删除</a>
                </td>
             </tr>
            {% endfor %}
        </tbody>
      </table>
    </div>

{#    分页#}
    <div class="clearfix">
        <ul class="pagination">
            {{ page_string }}
        </ul>
    </div>
</div>
{% endblock %}
  • 修改views.py
def pretty_list(request):
    '''靓号管理'''
    # # 获取所有靓号列表 [obj,obj,obj]
    # queryset = models.PrettyNum.objects.all()
    # return render(request, 'pretty_list.html', {'queryset': queryset})

    data_dict = {}
    search_result = request.POST.get('mobile_search', "")  # 绑定前端表单的搜索框'mobile_search',如果有值就返回“mobile_search”,如果没有就返回空“”
    if search_result:
        data_dict["mobile"] = search_result
        
    queryset = models.PrettyNum.objects.filter(**data_dict).order_by("-level")
    return render(request, 'pretty_list.html', {"queryset": queryset, "search_result": search_result})

14.ModelForm

将用户管理也采用ModelForm;

我们会发现,每个ModelForm的def _int _(self, *args, **kwargs)几乎都差不多:

因此,我们f封装我们的ModelForm:

自定义类BootStrapModelForm:

  • 在app01目录新建utils目录,并新建bootstrap.py

bootstrap.py

from django import forms

class BootstraoModelForm(forms.ModelForm):
    def __int__(self,*args,**kwargs):
        super().__int__(*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.py,将我们在views定义的ModelForm改写到form中
from django.forms import ModelForm
from app01 import models
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
from django import forms
from app01.utils.bootstrap import BootstraoModelForm

class PrettyAddModelForm(BootstraoModelForm):
    # xx = form.CharField*("...")  # ModelForm不仅支持数据库中支持的字段,我们也可以另外自定义

    # ''''验证1'''
    # '''
    #     【正则表达式】分析如下:
    #
    #     手机号都为11位,所以必须限定匹配的数字的位数,通过$来限定以9位数字结尾,
    #
    #     又因为手机号都以1开头,所以通过^1限定以1开头,
    #
    #     然后手机号第二位貌似只有3,5,6,7,8,这几个数字,所以通过[3,5,6,7,8]来匹配其中的任一数字,
    #
    #     最后{9}匹配9个/d。
    # '''
    # mobile = forms.CharField(
    #     label='手机号',
    #     # 创建手机号的正则校验器
    #     validators=[RegexValidator(r'^1[3-9]\d{9}$', '手机号码错误'), ],
    #
    # )

    class Meta:
        model = models.PrettyNum
        # fields = ["name", "password", "age", "xx"]
        fields = ["mobile", "price", "level", 'status']

        # 我们可以使用下面的方法去获取各个属性,但有个更方便的方法:重定义_init__
        # widgets = {
        #     "mobile": forms.TextInput(attrs={"class": "form-control"}),
        #     "price": forms.PasswordInput(attrs={"class": "form-control"}),
        #     "level": forms.TextInput(attrs={"class": "form-control"}),
        #     "status": forms.TextInput(attrs={"class": "form-control"}),
        # }

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

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

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

        # 验证通过,用户输入的值返回
        return txt_mobile
    # 验证:方式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 clean_mobile(self):
    #     # 验证手机号是否已经存在
    #     mobile = self.data.get('mobile')
    #     exists = models.PrettyNum.objects.filter(mobile=mobile)
    #
    #     if exists:
    #         raise ValidationError("手机号已存在")
    #     # 验证通过,用户输入的值返回
    #     return mobile


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

        # 验证:方式2

    def clean_mobile(self):
        txt_mobile = self.cleaned_data["mobile"]

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

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

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

   
  • 在views.py修改import
from app01.utils.form import PrettyAddModelForm,PrettyEditModelForm

实现user的ModelForm

  • form.py
class UserAddModelForm(BootstraoModelForm):
    name=forms.CharField(
        min_length=3,
        label='用户名',
        widget=forms.TextInput(attrs={'class':'form-control'})
    )
    class Meta:
        model=models.UserInfo
        fields=['name','password','age','account','create_time','gender','depart']

  • user_add.html

    替换为:

     <div class="panel-body">
            <form method="post" novalidate="novalidate">
                  {% csrf_token %}
                  {% for field in form %}
                      <div class="form-group">
                        <label>{{ field.label }}</label>
                        {{ field }}
                        <span style="color:#ff0000;">{{ field.errors.0 }}{# 错误信息有很多,显示errors.0即可 #}</span>
                      </div>
                  {% endfor %}
                  <button type="submit" class="btn btn-success">提交</button>
            </form>
          </div>
    

    整体的user_add.html:

    {% extends 'layout.html' %}
    {% load static %}
    
    {% block content %}
        <div class="container">
            <div class="panel panel-default">
                <div class="panel-heading">
                    <h3 class="panel-title">新建用户</h3>
                </div>
                <div class="panel-body">
                    <form method="post" novalidate="novalidate">
                          {% csrf_token %}
                          {% for field in form %}
                              <div class="form-group">
                                <label>{{ field.label }}</label>
                                {{ field }}
                                <span style="color:#ff0000;">{{ field.errors.0 }}</span>{# 错误信息有很多,显示errors.0即可 #}
                              </div>
                          {% endfor %}
                          <button type="submit" class="btn btn-success">提交</button>
                    </form>
                  </div>
    {#            <div class="panel-body">#}
    {#                <form method="post">#}
    {#                    {% csrf_token %}#}
    {#                      <div class="form-group">#}
    {#                            <label>姓名</label>#}
    {#                            <input type="text" class="form-control" placeholder="姓名" name="user">#}
    {#                      </div>#}
    {#                      <div class="form-group">#}
    {#                            <label>密码</label>#}
    {#                            <input type="text" class="form-control" placeholder="密码" name="pwd">#}
    {#                      </div>#}
    {#                      <div class="form-group">#}
    {#                            <label>年龄</label>#}
    {#                            <input type="text" class="form-control" placeholder="年龄" name="age">#}
    {#                      </div>#}
    {#                      <div class="form-group">#}
    {#                            <label>余额</label>#}
    {#                            <input type="text" class="form-control" placeholder="余额" name="ac">#}
    {#                      </div>#}
    {#                      <div class="form-group">#}
    {#                            <label>入职时间</label>#}
    {#                            <input  id="dt" type="text" class="form-control" placeholder="入职时间" name="ctime">#}
    {#                      </div>#}
    {#                      <div class="form-group">#}
    {#                            <label>性别</label>#}
    {#                            <select class="form-control" name="gd">#}
    {#                                {% for item in gender_choices %}#}
    {#                                    <option value="{{ item.0 }}">{{ item.1 }}</option>#}
    {#                                {% endfor %}#}
    {#                                <option value="1">男</option>#}
    {#                                <option value="2">女</option>#}
    {#                            </select>#}
    {#                      </div>#}
    {#                      <div class="form-group">#}
    {#                            <label>部门</label>#}
    {#                            <select class="form-control" name="dp">#}
    {#                                {% for item in depart_list %}#}
    {#                                    <option value="{{ item.id }}">{{ item.title }}</option>#}
    {#                                {% endfor %}#}
    {#                            </select>#}
    {#                      </div>#}
    {##}
    {#                      <button type="submit" class="btn btn-default">Submit</button>#}
    {#                </form>#}
    {#            </div>#}
            </div>
        </div>
    {% endblock %}
    
    
    
    
  • views.py

from app01.utils.form import UserAddModelForm

def user_add(request):
    '''添加用户'''
    # 1.处理GET请求
    # 在部门聊天界面点击’新建部门‘-----GET请求(去user_add.html)
    if request.method == 'GET':
        # context = {
        #     'gender_choices': models.UserInfo.gender_choices,
        #     'depart_list': models.Department.objects.all()
        # }
        # return render(request, 'user_add.html', context)
        form = UserAddModelForm()
        return render(request, 'user_add.html', {'form':form})

    form=UserAddModelForm(data=request.POST)
    if form.is_valid():
        form.save()
        return redirect("/user/list/")
    return render(request, 'pretty_add.html', {"form": form})
    # # 获取用户提交的数据
    # user = request.POST.get('user')
    # 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, create_time=ctime, gender=gender,
    #                                depart_id=depart_id)  # 写为depart=depart_id,新建时会报错
    # # 返回到用户列表页面
    # return redirect('/user/list/')

效果:

在这里插入图片描述

但是,user采用ModelForm时出现了一些问题:

Depart无法正常显示

原因:
Department object(1)是 models.py 中 Department 的对象

让他正常显示部门名称,解决方法就是本节开头说的,利用__str__魔法函数

models.py

    def __str__(self):
        return self.title

在这里插入图片描述

已解决:

在这里插入图片描述

15.靓号分页

1)新建足够多的数据数据

2)找分页组件的代码

在这里插入图片描述

3)分页

一次显示十个数据

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]
data = models.PrettyNum.objects.all().count()
data = models.PrettyNum.objects.filter(id=1).count()
  • 分页的逻辑和处理规则
封装分页类
  • 封装分页类

    • 从头到尾开发

    • 写项目用【pagination.py】公共组件。

  • 小Bug,搜索 + 分页情况下。

分页时候,保留原来的搜索条件

http://127.0.0.1:8000/pretty/list/?q=888
http://127.0.0.1:8000/pretty/list/?page=1

http://127.0.0.1:8000/pretty/list/?q=888&page=23

1)在utils目录下新建pagination.py

"""
自定义的分页组件,以后如果想要使用这个分页组件,你需要做如下几件事:

在视图函数中:
    def pretty_list(request):

        # 1.根据自己的情况去筛选自己的数据
        queryset = models.PrettyNum.objects.all()

        # 2.实例化分页对象
        page_object = Pagination(request, queryset)

        context = {
            "queryset": page_object.page_queryset,  # 分完页的数据
            "page_string": page_object.html()       # 生成页码
        }
        return render(request, 'pretty_list.html', context)

在HTML页面中

    {% for obj in queryset %}
        {{obj.xx}}
    {% endfor %}

    <ul class="pagination">
        {{ page_string }}
    </ul>

"""

from django.utils.safestring import mark_safe


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中传递的获取分页的参数,例如:/etty/list/?page=12
        :param plus: 显示当前页的 前或后几页(页码)
        """

        from django.http.request import QueryDict
        import copy
        query_dict = copy.deepcopy(request.GET)
        query_dict._mutable = True
        self.query_dict = query_dict

        self.page_param = page_param
        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 = (page - 1) * page_size
        self.end = page * 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页
        if self.total_page_count <= 2 * self.plus + 1:
            # 数据库中的数据比较少,都没有达到11页。
            start_page = 1
            end_page = self.total_page_count
        else:
            # 数据库中的数据比较多 > 11页。

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

        page_str_list.append(search_string)
        page_string = mark_safe("".join(page_str_list))
        return page_string


  • views.py
from app01.utils.pagination import Pagination

def pretty_list(request):
    '''靓号管理'''
    # # 获取所有靓号列表 [obj,obj,obj]
    # queryset=models.PrettyNum.objects.all()
    # return render(request,'pretty_list.html',{'queryset':queryset})

    data_dict={}
    search_result = request.POST.get('mobile_search', "")  # 如果有值就返回“q”,如果没有就返回空“”
    if search_result: #搜索
        data_dict["mobile"] = search_result
        queryset = models.PrettyNum.objects.filter(**data_dict)
        return render(request, 'pretty_list.html', {'queryset':queryset})
    else:#分页
        queryset = models.PrettyNum.objects.filter(**data_dict)
        page_object=Pagination(request,queryset)
        print("queryset:",queryset)
        print("page_object:", page_object)
        context={
            'search_result':search_result,

            'queryset':page_object.page_queryset,# 分完页的数据
            'page_string':page_object.html() # 页码
        }
        return render(request,'pretty_list.html',context)

  • pretty_list.html

在这里插入图片描述

效果:

在这里插入图片描述

16.时间插件

问题:填写入职时间很麻烦且容易出错,所以要引入时间插件

  • 首先,引入插件

在母版那修改一下,使子板可以自定义引入css、js

{% block css %}{% endblock %}
{% block js %}{% endblock %}

  • layout.html
{% load static %}
<!DOCTYPE html>
<html lang="en">
{% block css %}{% endblock %}

<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.css'%}">
    <style>

    </style>
</head>
<body>
{# 导航条部分 #}
<nav class="navbar navbar-default">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapse"
                    data-toggle="collapse" data-target="#bs-example-navbar-collaspe-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>
            <a class="navbar-brand" href="/user/list">用户管理</a>
            <a class="navbar-brand" href="/depart/list">部门管理</a>
            <a class="navbar-brand" href="/pretty/list">靓号管理</a>
        </div>

        <div class="collapse navbar-collaspe" 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="#">Link</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">云浮市</a>
                    <ul class="dropdown-menu">
                        <li><a href="#">个人资料</a> </li>
                        <li><a href="#">我的信息</a> </li>
                        <li role="separator" class="divider"> </li>
                        <li><a href="#">注销</a> </li>
                     </ul>
                </li>

            </ul>

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

<div>
    {% block content %} {% endblock %}
</div>
{% block js %}
    <script src="{% static 'js/jquery-3.6.0.min.js' %}"></script>
    <script src="{% static 'plugins/bootstrap-3.4.1/js/bootstrap.min.js'%}"></script>
{% endblock %}

</body>
</html>
  • user_add.html

在这里插入图片描述

实现(具体去改子板内容时会遇到两种情况):

  • 使用ModelForm

  • 未使用ModelForm

  • 如果没使用ModelForm,指定“id”即可使用我们写的js函数

在这里插入图片描述

  • 若使用了ModelForm,怎么引入id呢?

我们发现,使用 ModelForm 的 id 会有一个规律,其 id = "id_" + "name"

在这里插入图片描述

那就简单了,直接改一下之前写的 js函数 就好了:

在这里插入图片描述

效果:

在这里插入图片描述

最后,用同样的方式来修改,编辑用户部分

17.整理文件

  • views/user.py
from app01.utils.form import UserEditModelForm,UserAddModelForm
from django.shortcuts import render, redirect
from app01 import models

def user_list(request):
    '''用户管理'''
    # 获取所有用户列表 [obj,obj,obj]
    queryset = models.UserInfo.objects.all()
    '''
    # 用Python的语法获取数据
    for obj in queryset:
        print(obj.id, obj.name, obj.account, obj.create_time.strftime("%Y-%m-%d"), obj.gender, obj.get_gender_display(),
              obj.depart_id, obj.depart.title)
        # print(obj.name, obj.depart_id)
        # obj.depart_id  # 获取数据库中存储的那个字段值
        # obj.depart.title  # 根据id自动去关联的表中获取哪一行数据depart对象。
    '''
    return render(request, 'user_list.html', {'queryset': queryset})


def user_add(request):
    '''添加用户'''
    # 1.处理GET请求
    # 在部门聊天界面点击’新建部门‘-----GET请求(去user_add.html)
    if request.method == 'GET':
        # context = {
        #     'gender_choices': models.UserInfo.gender_choices,
        #     'depart_list': models.Department.objects.all()
        # }
        # return render(request, 'user_add.html', context)
        form = UserAddModelForm()
        return render(request, 'user_add.html', {'form':form})

    form=UserAddModelForm(data=request.POST)
    if form.is_valid():
        form.save()
        return redirect("/user/list/")
    return render(request, 'pretty_add.html', {"form": form})
    # # 获取用户提交的数据
    # user = request.POST.get('user')
    # 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, create_time=ctime, gender=gender,
    #                                depart_id=depart_id)  # 写为depart=depart_id,新建时会报错
    # # 返回到用户列表页面
    # return redirect('/user/list/')


def user_delete(request):
    '''删除用户'''
    # 获取ID
    # 'https//127.0.0.1:8000/user/delete/?nid=1'
    nid = request.GET.get('nid')
    # 删除
    models.UserInfo.objects.filter(id=nid).delete()
    # 重定向回用户列表
    return redirect('/user/list/')


def user_edit(request, nid):
    '''编辑用户'''
    row_object = models.UserInfo.objects.filter(id=nid).first()
    if request.method == "GET":
        # 根据nid,获得他的数据
        row_object = models.UserInfo.objects.filter(id=nid).first()
        context = {
            'form':UserEditModelForm(instance=row_object),
            'row_object': row_object
        }
        return render(request, 'user_edit.html', context)
    form = UserEditModelForm(data=request.POST, instance=row_object)

    if form.is_valid():
        form.save()
        return redirect('/user/list/')

    return render(request, 'user_edit.html', {'form': form})
    # # 处理post
    # # 获取用户提交的数据
    # user = request.POST.get('user')
    # 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.filter(id=nid).update(name=user, password=pwd, age=age, account=account, create_time=ctime,
    #                                               gender=gender, depart=depart_id)
    #
    # return redirect('/user/list/')
  • views/depart.py
from django.shortcuts import render, redirect
from app01 import models

def depart_list(request):
    """部门列表"""

    # 去数据库中获取所有的部门列表
    # 通过导入模块的方式直接获取所有表,存入queryset,省的表多的时候一个一个导入太麻烦了
    queryset = models.Department.objects.all()  # queryset 形式就是一个一个对象 [对象,对象,对象]

    return render(request, "depart_list.html", {'queryset': queryset})


def depart_add(request):
    '''添加部门'''
    # 1.处理GET请求
    # 在部门聊天界面点击’新建部门‘-----GET请求(去deaprt_add.html)
    if request.method == 'GET':
        return render(request, 'depart_add.html')

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

    # 3. 保存到数据库
    models.Department.objects.create(title=title)
    # 4. 重定向回部门列表
    return redirect('/depart/list')


def depart_delete(request):
    '''删除部门'''
    # 获取ID
    # 'https//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,获得他的数据
        row_object = models.Department.objects.filter(id=nid).first()
        print(row_object.id, row_object.title)

        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/")
  • views/pretty.py
from app01.utils.form import PrettyAddModelForm,PrettyEditModelForm
from django.shortcuts import render, redirect
from app01 import models
from app01.utils.pagination import Pagination
# def pretty_list(request):
#     '''靓号管理'''
#     # # 获取所有靓号列表 [obj,obj,obj]
#     # queryset = models.PrettyNum.objects.all()
#     # return render(request, 'pretty_list.html', {'queryset': queryset})
#
#     data_dict = {}
#     search_result = request.POST.get('mobile_search', "")  # 绑定前端表单的搜索框'mobile_search',如果有值就返回“mobile_search”,如果没有就返回空“”
#     if search_result:
#         data_dict["mobile"] = search_result
#
#     queryset = models.PrettyNum.objects.filter(**data_dict).order_by("-level")
#     return render(request, 'pretty_list.html', {"queryset": queryset, "search_result": search_result})

def pretty_list(request):
    '''靓号管理'''
    # # 获取所有靓号列表 [obj,obj,obj]
    # queryset=models.PrettyNum.objects.all()
    # return render(request,'pretty_list.html',{'queryset':queryset})

    data_dict={}
    search_result = request.POST.get('mobile_search', "")  # 如果有值就返回“q”,如果没有就返回空“”
    if search_result: #搜索
        data_dict["mobile"] = search_result
        queryset = models.PrettyNum.objects.filter(**data_dict)
        return render(request, 'pretty_list.html', {'queryset':queryset})
    else:#分页
        queryset = models.PrettyNum.objects.filter(**data_dict)
        page_object=Pagination(request,queryset)
        print("queryset:",queryset)
        print("page_object:", page_object)
        context={
            'search_result':search_result,

            'queryset':page_object.page_queryset,# 分完页的数据
            'page_string':page_object.html() # 页码
        }
        return render(request,'pretty_list.html',context)


def pretty_add(request):
    if request.method == 'GET':
        form = PrettyAddModelForm()
        return render(request, 'pretty_add.html', {"form": form})

    # 用户POST提交数据,数据效验(不能为空)
    # 如果数据合法,提交到数据库
    form = PrettyAddModelForm(data=request.POST)
    if form.is_valid():
        # print(form.cleaned_data)
        # 将数据存储至数据库
        # 方法一:老套路
        # models.PrettyNum.objects.create(...)
        # 方法二:ModelForm
        form.save()
        return redirect("/pretty/list/")
    # else:
    #     print(form.errors)

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


def pretty_delete(request):
    '''删除靓号'''
    # 获取ID
    nid = request.GET.get('nid')
    models.PrettyNum.objects.filter(id=nid).delete()
    return redirect('/pretty/list/')


def pretty_edit(request, nid):
    '''编辑靓号'''
    row_object = models.PrettyNum.objects.filter(id=nid).first()
    if request.method == 'GET':
        context = {
            'row_object': row_object,
            'form': PrettyEditModelForm(instance=row_object)
        }
        return render(request, 'pretty_edit.html', context)

    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})
  • 删除原来的views.py或者改名

  • urls.py

from django.contrib import admin
from django.urls import path
from app01.views import depart,user,pretty

urlpatterns = [
    # path('admin/', admin.site.urls),

    #部门管理
    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/delete/',user.user_delete),
    path('user/<int:nid>/edit/',user.user_edit),

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

在这里插入图片描述

18管理员操作

1)表结构:

在这里插入图片描述

  • models.py
class Admin(models.Model):
    """ 管理员 """
    username = models.CharField(verbose_name="用户名", max_length=32)
    password = models.CharField(verbose_name="密码", max_length=64)

    def __str__(self):
        return self.username

生成数据表:

python manage.py makemigrations
python manage.py migrate

插入数据:

insert into app01_admin(username, password) values("coderz","123456");

18.1管理员列表

  • layout.html

    在这里插入图片描述

  • admin_list.html

{% extends 'layout.html' %}

{% block content %}
    {# 用户列表 #}
    <div class="container">
        <div style="margin-bottom: 10px" class="clearfix">
            <a class="btn btn-success" href="/admin/add/">
                {# 可以添加一个 target="_blank" : 使页面在新页面打开,如果不设置,会在原页面打开(这里我们还是在当前页面打开) #}
                <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="Search for..."
                               value="{{ search_result }}">
                        <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="panel panel-default">
            <div class="panel-heading">
                <span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>
                管理员列表
            </div>

            <!-- 表格部分 -->
            <table class="table table-bordered">
                <thead>
                <tr>
                    <th>ID</th>
                    <th>用户名</th>
                    <th>密码</th>
                    <th>操作</th>
                </tr>
                </thead>
                <tbody>
                {% for obj in queryset %}
                    <tr>
                        <th>{{ obj.id }}</th>
                        <td>{{ obj.username }}</td>
                        <td>***********</td>
                        {# 密码不显示明文 #}
                        <td>
                            <a class="btn btn-default btn-xs" href="/admin/{{ obj.id }}/reset/">重置密码</a>
                            <a class="btn btn-primary btn-xs" href="/admin/{{ obj.id }}/edit/">编辑</a>
                            <a class="btn btn-danger btn-xs" href="/admin/delete/?nid={{ obj.id }}">删除</a>
                        </td>
                    </tr>
                {% endfor %}
                </tbody>
            </table>
        </div>

        {# 分页 #}
        <div class="clearfix">
            <ul class="pagination">
                {{ page_string }}
            </ul>
        </div>

    </div>



{% endblock %}

  • admin.py
from django.shortcuts import render, redirect
from app01 import models
from app01.utils.pagination import Pagination

def admin_list(request):
    '''管理员列表'''
    data_dict={}
    search_result=request.GET.get('q','')
    if search_result:
        data_dict['username']=search_result
    queryset=models.Admin.objects.all()
    page_object=Pagination(request,queryset)
    context={
        "search_data":search_result,
        'queryset':queryset,
        'page_string':page_object.html()#页码
    }
    return render(request,'admin_list.html',context)


  • utils/encrypt.py
from django.conf import settings
import hashlib


def md5(data_string):
    obj = hashlib.md5(settings.SECRET_KEY.encode('utf-8'))
    obj.update(data_string.encode('utf-8'))
    return obj.hexdigest()


  • urls.py
from app01 import admin

path('admin/list/',admin.admin_list),

18.2新建管理员

  • admin_add.html
<head>
    <meta charset="utf-8">
</head>

{% extends 'layout.html' %}

{% block content %}
<div class="container">
    <div class="panel panel-default">
      <!-- Default panel contents -->
      <div class="panel-heading">
          <h3 class="panel-title">新建管理员</h3>
      </div>
      <div class="panel-body">
        <form method="post" novalidate="novalidate">
              {% csrf_token %}
              {% for field in form %}
                  <div class="form-group">
                    <label>{{ field.label }}</label>
                    {{ field }}
                    <span style="color:#ff0000;">{{ field.errors.0 }}</span>
                  </div>
              {% endfor %}
              <button type="submit" class="btn btn-success">提交</button>
        </form>
      </div>


    </div>
</div>
{% endblock %}
  • urls.py
path("admin/add/", admin.admin_add),
  • admin.py
from django.shortcuts import redirect,render
from app01 import models
from app01.utils.pagination import Pagination
from app01.utils.bootstrap import BootstraoModelForm
from django import forms
from app01.utils.encrypt import md5
from django.core.exceptions import ValidationError

class AdminAddModelForm(BootstraoModelForm):
    confirm_password=forms.CharField(
        label='确认密码',
        widget=forms.PasswordInput(render_value=True)
    )

    class Meta:
        model=models.Admin
        fields=['username','password','confirm_password']
        widgets={
            'password':forms.PasswordInput(render_value=True)
        }

    #返回密文(数据加密)
    def clean_password(self):
        pwd=self.cleaned_data.get('password')
        return md5(pwd)

    def clean_confirm_password(self):
        pwd=self.cleaned_data.get('password')
        confirm=md5(self.cleaned_data.get("confirm_password"))
        if confirm !=pwd:# 密文之间进行比较
            raise ValidationError('密码不一致')
        # 返回什么,此字段以后保存到数据库就是什么。
        return confirm
        
        
        
 
 
 def admin_add(request):
    """ 添加管理员 """

    if request.method=='GET':
        form=AdminAddModelForm()
        return render(request,'admin_add.html',{'form':form})

    form =AdminAddModelForm(data=request.POST)
    if form.is_valid():
        form.save()
        return redirect('/admin/list/')
    return render(request,'admin_add.html',{'form':form})

18.3编辑管理员

  • admin_edit.html
<head>
    <meta charset="utf-8">
</head>

{% extends 'layout.html' %}

{% block content %}
<div class="container">
    <div class="panel panel-default">
      <!-- Default panel contents -->
      <div class="panel-heading">
          <h3 class="panel-title">编辑管理员</h3>
      </div>
      <div class="panel-body">
        <form method="post" novalidate="novalidate">
              {% csrf_token %}
              {% for field in form %}
                  <div class="form-group">
                    <label>{{ field.label }}</label>
                    {{ field }}
                    <span style="color:#ff0000;">{{ field.errors.0 }}</span>
                  </div>
              {% endfor %}
              <button type="submit" class="btn btn-success">提交</button>
        </form>
      </div>


    </div>
</div>
{% endblock %}
  • urls.py
path("admin/<int:nid>/edit/", admin.admin_edit),
  • admin.py
class AdminEditModelForm(BootstraoModelForm):
    class Meta:
        model=models.Admin
        fields=['username']
        

def admin_edit(request,nid):
    row_object=models.Admin.objects.filter(id=nid).first()
    if not row_object: # 判断是否为空
        # return render(request, 'error.html', {"msg": "数据不存在"})
        return redirect('/admin/list')

    if request.method=='GET':
        form =AdminEditModelForm(instance=row_object)
        return render(request, 'admin_edit.html', {'form': form})

    form = AdminEditModelForm(data=request.POST, instance=row_object)
    if form.is_valid():
        form.save()
        return redirect('/admin/list/')
    return render(request, 'admin_edit.html', {"form": form})

18.4删除

  • urls.py
path('admin/delete/',admin.admin_delete),
  • admin.py
def admin_delete(request):
    nid = request.GET.get('nid')
    models.Admin.objects.filter(id=nid).delete()
    return redirect('/admin/list/')

18.5重置密码

  • urls.py
path("admin/<int:nid>/reset/", admin.admin_reset),
  • admin.py
class AdminResetModelForm(BootstraoModelForm):
    confirm_password = forms.CharField(
        label='确认密码',
        widget=forms.PasswordInput(render_value=True)
    )

    class Meta:
        model = models.Admin
        fields = ['username', 'password', 'confirm_password']
        widgets = {
            'password': forms.PasswordInput(render_value=True)
        }

    # 返回密文(数据加密)
    def clean_password(self):
        pwd = self.cleaned_data.get('password')
        md5_pwd=md5(pwd)

        # 去数据库校验当前密码和新输入的密码是否一致
        exists=models.Admin.objects.filter(id=self.instance.pk,password=md5_pwd).exists()
        if exists:
            raise ValidationError("不能与以前的密码相同")
        return md5_pwd

    def clean_confirm_password(self):
        pwd = self.cleaned_data.get('password')
        confirm = md5(self.cleaned_data.get("confirm_password"))
        if confirm != pwd:  # 密文之间进行比较
            raise ValidationError('密码不一致')
        # 返回什么,此字段以后保存到数据库就是什么。
        return confirm





def admin_reset(request,nid):
    """ 重置密码 """
    row_object=models.Admin.objects.filter(id=nid).first()
    if not row_object:
        redirect('/admin/list/')

    if request.method=='GET':
        form=AdminResetModelForm(instance=row_object)
        return render(request,'admin_reset.html',{'form':form})

    form=AdminResetModelForm(data=request.POST,instance=row_object)
    if form.is_valid():
        form.save()
        return redirect('/admin/list/')
    return render(request, 'admin_reset.html', {'form': form})
  • admin_list.html

在这里插入图片描述

  • admin_reset.html
<head>
    <meta charset="utf-8">
</head>

{% extends 'layout.html' %}

{% block content %}
<div class="container">
    <div class="panel panel-default">
      <!-- Default panel contents -->
      <div class="panel-heading">
          <h3 class="panel-title">密码重置</h3>
      </div>
      <div class="panel-body">
        <form method="post" novalidate="novalidate">
              {% csrf_token %}
              {% for field in form %}
                  <div class="form-group">
                    <label>{{ field.label }}</label>
                    {{ field }}
                    <span style="color:#ff0000;">{{ field.errors.0 }}</span>
                  </div>
              {% endfor %}
              <button type="submit" class="btn btn-success">提交</button>
        </form>
      </div>


    </div>
</div>
{% endblock %}

19.用户认证(登录)

转载文章:Django 05 :管理员操作 + 用户认证【 中间件 +图片验证码 + Cookie 与 Session】

版权声明:本文为CSDN博主「DLNovice」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_55535816/article/details/123226642

实现用户登录前,我们先补充些知识

19.1、Cookie 和 Session

**会话(Session)*跟踪是Web程序中常用的技术,用来*跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份Session通过在服务器端记录信息确定用户身份

下面段解释转自: Cookie和Session是什么?它们的区别是什么?_qichangjian的博客-CSDN博客_cookie和session

什么是Cookie?
Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端会把Cookie保存起来。

当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。
信息保存的时间可以根据需要设置.

如果没有设置Cookie失效日期,它们仅保存到关闭浏览器程序为止. 如果将Cookie对象的Expires属性设置为Minvalue,则表示Cookie永远不会过期.

Cookie存储的数据量很受限制,大多数浏览器支持最大容量为4K,因此不要用来保存数据集及其他大量数据.

由于并非所有的浏览器都支持Cookie,并且数据信息是以明文文本的形式保存在客户端的计算机中,
因此最好不要保存敏感的,未加密的数据,否则会影响网站的安全性

什么是Session?
Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是Session。客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。

每个用户访问服务器都会建立一个session,那服务器是怎么标识用户的唯一身份呢?事实上,用户与服务器建立连接的同时,服务器会自动为其分配一个SessionId。

Session和Cookie的区别?
1、数据存储位置:cookie数据存放在客户的浏览器上,session数据放在服务器上。

2、安全性:cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,考虑到安全应当使用session。

3、服务器性能:session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用cookie。

4、数据大小:单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。

5、信息重要程度:可以考虑将登陆信息等重要信息存放为session,其他信息如果需要保留,可以放在cookie中。

下面我们简单从

【http无状态短连接】

在这里插入图片描述

左:浏览器,右:网站

  • 短链接 ==> 浏览器发送【请求】,网站返回【响应】,然后链接就断开了
  • 之后再发送请求,网站就不知道你是谁了

那么咋整呢? Cookie 和 Session

http://127.0.0.1:8000/admin/list/ https://127.0.0.1:8000/admin/list/

在这里插入图片描述

我们基于Cookie 和 Session就可以实现用户登录和认证

19.2用户认证—基本实现

  • login.html
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.min.css' %}">
    <style>
        .account {
            width: 400px;
            border: 1px solid #dddddd;
            border-radius: 5px;
            box-shadow: 5px 5px 20px #aaa;

            margin-left: auto;
            margin-right: auto;
            margin-top: 100px;
            padding: 20px 40px;
        }

        .account h2 {
            margin-top: 10px;
            text-align: center;
        }
    </style>
</head>
<body>
<div class="account">
    <h2>用户登录</h2>
    <form method="post" novalidate>
        {% csrf_token %}
        <div class="form-group">
            <label>用户名</label>
            {{ form.username }}
            <span style="color: red;">{{ form.username.errors.0 }}</span>
        </div>
        <div class="form-group">
            <label>密码</label>
            {{ form.password }}
            <span style="color: red;">{{ form.password.errors.0 }}</span>
        </div>

        <input type="submit" value="登 录" class="btn btn-primary">
    </form>
</div>


</body>
</html>


  • urls.py
path('login/', account.login),
  • account.py
from django.shortcuts import render, HttpResponse, redirect
from django import forms
from app01 import models
from app01.utils.bootstrap import BootStrapForm
from app01.utils.encrypt import md5


class LoginForm(BootStrapForm):
    username = forms.CharField(
        label="用户名",
        widget=forms.TextInput,
        required=True
    )
    password = forms.CharField(
        label="密码",
        widget=forms.PasswordInput(render_value=True),
        required=True
    )
	

    class Meta:
        model=models.Admin
        fields=['username','password',]
        
    def clean_password(self):
        pwd = self.cleaned_data.get("password")
        return md5(pwd)


def login(request):
    """ 登录 """
    if request.method == "GET":
        form = LoginForm()
        return render(request, 'login.html', {'form': form})

    form = LoginForm(data=request.POST)
    if form.is_valid():
        # 去数据库校验用户名和密码是否正确,获取用户对象、若错误则为空返回None
        # admin_object = models.Admin.objects.filter(username=xxx, password=xxx).first()
        admin_object = models.Admin.objects.filter(**form.cleaned_data).first()
        if not admin_object:
            form.add_error("password", "用户名或密码错误")  # 在“密码”输入框下显示【用户名或密码错误】
            # form.add_error("username", "用户名或密码错误")  # 在“用户名”输入框下显示【用户名或密码错误】
            return render(request, 'login.html', {'form': form})

        # 用户名和密码正确
        # 网站生成随机字符串; 写到用户浏览器的cookie中;在写入到session中;
        request.session["info"] = {'id': admin_object.id, 'name': admin_object.username}
        # session可以保存7天
        request.session.set_expiry(60 * 60 * 24 * 7)

        return redirect("/admin/list/")

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


在这里插入图片描述

19.3体验中间件

若用户未登录,那么除了【登录界面】,访问其他界面都会自动跳转到【登录界面】

目标:在所有视图函数前面统一加入判断。

info = request.session.get("info")
if not info:
 return redirect('/login/')

使用Django的【中间件】

实际上我们发送请求到视图函数def index(request): ...会经过几个中间件(类),类中定义了process_request方法

也就是,请求来了要执行一个一个类中的process_request方法

在这里插入图片描述

我们app01创个文件夹体验一下【中间件】

  • 定义中间件 app01/middleware/auth.py
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse


class M1(MiddlewareMixin):
    """ 中间件1 """

    def process_request(self, request):
        # 如果方法中没有返回值(返回None),继续向后走
        # 如果有返回值 HttpResponse、render 、redirect
        print("M1.process_request")
        return HttpResponse("无权访问")

    def process_response(self, request, response):
        print("M1.process_response")
        return response


class M2(MiddlewareMixin):
    """ 中间件2 """

    def process_request(self, request):
        print("M2.process_request")

    def process_response(self, request, response):
        print("M2.process_response")
        return response


  • 应用中间件(在 setings.py中注册中间件)
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'app01.middleware.auth.M1',
    'app01.middleware.auth.M2',
]

  • 在中间件的process_request方法
# 如果方法中没有返回值(返回None),继续向后走
# 如果有返回值 HttpResponse、render 、redirect,则不再继续向后执行。

在这里插入图片描述

19.4中间件实现【登录校验】

  • 编写中间件 app01/middleware/auth.py
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse, redirect


class AuthMiddleware(MiddlewareMixin):

    def process_request(self, request):
        # 0.排除那些不需要登录就能访问的页面
        #   request.path_info 获取当前用户请求的URL /login/
        if request.path_info == "/login/":
            return

        # 1.读取当前访问的用户的session信息,如果能读到,说明已登陆过,就可以继续向后走。
        info_dict = request.session.get("info")
        print(info_dict)
        if info_dict:
            return

        # 2.没有登录过,重新回到登录页面
        return redirect('/login/')

在这里插入图片描述

在这里插入图片描述

  • 应用中间件
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'app01.middleware.auth.AuthMiddleware',
]

然后,我们删掉以前的Cookie(勿忘!!!!!!!)

在这里插入图片描述

在这里插入图片描述

然后访问http://127.0.0.1:8000/depart/list,就会自动跳转到【登录界面】

19.5、注销

  • account.py
def logout(request):
    """ 注销 """
    request.session.clear()
    return redirect('/login/')

  • layout.html
<li><a href="/logout/">注销</a></li>

  • urls.py
path('logout/', account.logout),

  • account.py

from django.shortcuts import render, HttpResponse, redirect
from django import forms
from app01 import models
from app01.utils.bootstrap import BootStrapForm
from app01.utils.encrypt import md5


class LoginForm(BootStrapForm):
    username = forms.CharField(
        label="用户名",
        widget=forms.TextInput,
        required=True
    )
    password = forms.CharField(
        label="密码",
        widget=forms.PasswordInput(render_value=True),
        required=True
    )
	

    class Meta:
        model=models.Admin
        fields=['username','password',]
        
    def clean_password(self):
        pwd = self.cleaned_data.get("password")
        return md5(pwd)


def login(request):
    """ 登录 """
    if request.method == "GET":
        form = LoginForm()
        return render(request, 'login.html', {'form': form})

    form = LoginForm(data=request.POST)
    if form.is_valid():
        # 去数据库校验用户名和密码是否正确,获取用户对象、若错误则为空返回None
        # admin_object = models.Admin.objects.filter(username=xxx, password=xxx).first()
        admin_object = models.Admin.objects.filter(**form.cleaned_data).first()
        if not admin_object:
            form.add_error("password", "用户名或密码错误")  # 在“密码”输入框下显示【用户名或密码错误】
            # form.add_error("username", "用户名或密码错误")  # 在“用户名”输入框下显示【用户名或密码错误】
            return render(request, 'login.html', {'form': form})

        # 用户名和密码正确
        # 网站生成随机字符串; 写到用户浏览器的cookie中;在写入到session中;
        request.session["info"] = {'id': admin_object.id, 'name': admin_object.username}
        # session可以保存7天
        request.session.set_expiry(60 * 60 * 24 * 7)

        return redirect("/admin/list/")

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



在这里插入图片描述

19.3体验中间件

若用户未登录,那么除了【登录界面】,访问其他界面都会自动跳转到【登录界面】

目标:在所有视图函数前面统一加入判断。

info = request.session.get("info")
if not info:
 return redirect('/login/')

使用Django的【中间件】

实际上我们发送请求到视图函数def index(request): ...会经过几个中间件(类),类中定义了process_request方法

也就是,请求来了要执行一个一个类中的process_request方法

在这里插入图片描述

我们app01创个文件夹体验一下【中间件】

  • 定义中间件 app01/middleware/auth.py
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse


class M1(MiddlewareMixin):
    """ 中间件1 """

    def process_request(self, request):
        # 如果方法中没有返回值(返回None),继续向后走
        # 如果有返回值 HttpResponse、render 、redirect
        print("M1.process_request")
        return HttpResponse("无权访问")

    def process_response(self, request, response):
        print("M1.process_response")
        return response


class M2(MiddlewareMixin):
    """ 中间件2 """

    def process_request(self, request):
        print("M2.process_request")

    def process_response(self, request, response):
        print("M2.process_response")
        return response


  • 应用中间件(在 setings.py中注册中间件)
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'app01.middleware.auth.M1',
    'app01.middleware.auth.M2',
]

  • 在中间件的process_request方法
# 如果方法中没有返回值(返回None),继续向后走
# 如果有返回值 HttpResponse、render 、redirect,则不再继续向后执行。

在这里插入图片描述

19.4中间件实现【登录校验】

  • 编写中间件 app01/middleware/auth.py
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse, redirect


class AuthMiddleware(MiddlewareMixin):

    def process_request(self, request):
        # 0.排除那些不需要登录就能访问的页面
        #   request.path_info 获取当前用户请求的URL /login/
        if request.path_info == "/login/":
            return

        # 1.读取当前访问的用户的session信息,如果能读到,说明已登陆过,就可以继续向后走。
        info_dict = request.session.get("info")
        print(info_dict)
        if info_dict:
            return

        # 2.没有登录过,重新回到登录页面
        return redirect('/login/')

在这里插入图片描述

在这里插入图片描述

  • 应用中间件
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'app01.middleware.auth.AuthMiddleware',
]

然后,我们删掉以前的Cookie(勿忘!!!!!!!)
在这里插入图片描述

在这里插入图片描述

然后访问http://127.0.0.1:8000/depart/list,就会自动跳转到【登录界面】

19.5、注销

  • account.py
def logout(request):
    """ 注销 """
    request.session.clear()
    return redirect('/login/')

  • layout.html
<li><a href="/logout/">注销</a></li>

  • urls.py
path('logout/', account.logout),

具体见视频:2022 B站最详细django3教程(django从入门到实践)_哔哩哔哩_bilibili

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值