Django

django, web框架, 别人写好了一些代码,可以在它的基础上实现网站等。

Django深入_django

python 中常见的 web 框架

  • Django, 大而全。内部为我们提供了很多方便的组件, 直接调用(积极支持异步非阻塞)。
  • Flask, 小而精。 自己内容提供的少 + 第三方组件。
  • tornado/sanic/fastapi, 小而精, 自己内部提供的少 + 第三方组件 + 异步非阻塞。

1.环境搭建

# 安装最新版本的 django
# pip install django

# 安装3 版本的 django
# pip install django==3

Django深入_django_02

Django深入_html_03

Django深入_html_04

Django深入_django_05

Django深入_中间件_06

pycharm 导致的bug, 可能的 bug, 很早之前的问题了,现在应该没了,但是记录下

Django深入_django_07

如果django 中的 setttings.py 中的 templates 列表中的 是这样的话,

'DIRS' :[os.path.join(BASR_DIR, 'templates')],

需要自己引入 os 模块 import os

pycharm 方式启动项目

Django深入_中间件_08

app

在django 项目中创建app, 在app 中编写项目中的具体的业务。

Django深入_django_09

使用如下命令 创建相关的app

# python manage.py startapp web

Django深入_html_10

Django深入_html_11

总结

Django深入_html_12

Django深入_中间件_13

Django深入_django_14

Django深入_django_15

2.视图函数

Django深入_django_16

Django深入_html_17

Django深入_中间件_18

3.模版

可以通过 传递参数 传递变量到html 模版中

# web(app)/views.py

from django.shortcuts import render
from django.shortcuts import HttpResponse
# Create your views here.


def login(request):
    context = {"username": "mpp", 1: 18, "age": 23, "list1":[1, 2, 3]}
    # return HttpResponse("hello, world")
    return render(request, "login.html", context=context)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{# 获取变量 #}
{{ username }}

{# for 循环#}
{% for item in list1 %}
    <li>item</li>
{% endfor %}

{#if 条件#}
{% if username %}
    <h1></h1>
{% endif %}


</body>
</html>

4.静态文件处理

图片, css, js 等

Django深入_中间件_19

Django深入_html_20

5.ORM-表结构

ORM, 对象关系映射

Django深入_django_21

创建数据库, 使用 navicat 或者 数据库命令行创建数据库。

Django深入_html_22

Django深入_html_23

Django深入_html_24

给某个已经存在的数据表添加新的列, 由于之前的数据行没有相关的列,需要 设置 新加的列可以为空或者新加的 列的默认值。

Django深入_中间件_25

6.ORM-数据操作

Django深入_中间件_26

新增 models.UserInfo.objects.create(), 上面图片中少了 objects。

Django深入_html_27

Django深入_中间件_28

7.cookie 和 session

用户登录相关。

Django深入_django_29

登录成功,在session 中存储 登录信息

Django深入_中间件_30

Django深入_django_31

Django深入_html_32

访问 用户详情页, 用户未登录, 无法访问,跳转到登录页面, 用户已登录,可以访问,

页面访问限制,用户session 校验。

Django深入_中间件_33

8.中间件

Django深入_html_34

在客户端发送请求后, 请求到达 视图函数之前, 需要经过一些中间件的处理,在视图函数返回response 时,数据 也会 经过一些中间件的处理。

在 django 项目中的 settings.py 中的 MIDDLEWARE 列表中就是一些已经定义好的 中间件。

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',
]

自定义中间件

可以参考原本写好的中间件的内容

Django深入_django_35

pycharm ctrl + 鼠标点击可以查看源码,

如下图所示中间件继承了 MiddlewareMixin类, 且内部有一个 process_reqeust 函数 和一个 process_response 函数。

Django深入_django_36

自定义写自己的 中间件

Django深入_html_37

# add_middleware.py 

from django.utils.deprecation import MiddlewareMixin


class my_middleware(MiddlewareMixin):
    def process_request(self, request):
        print("process_rqeust")

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

将写好的中间 件路径添加到 settings.py 的 middleware 列表中。

Django深入_html_38

MIDDLEWARE = [
    'ext.add_middleware.my_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',
]

任意访问一个视图函数, 触发了写好的中间件

Django深入_django_39

django 项目中 settings.py 中 ,中间件的放置前后顺序是 对项目有影响的, 每一次中间件添加或者更改了一些内容, 如 request.session 就是 session 相关的中间处理的,如果想在自己的中间件中使用 reqeust.session 的话, 那么自定义的中间件 应该 放到 session 处理相关的中间件的后面, 通常 自定定义的中间件写在 中间件列表的最后。

如下:

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',
    'ext.add_middleware.my_middleware',    # 自定义的中间件
]

中间件的特点 : 所有的请求都会经过中间件。

9.中间件解决登录繁琐的问题

问题:假设 项目中写了 100 个 url 对应有 100 个视图函数, 有 99 个需要登录成功之后才可以查看。

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import redirect


class my_middleware(MiddlewareMixin):
    def process_request(self, request):
        print("process_rqeust")
        # 无返回值或者 返回 None , 继续往后走。
        # 有返回值 redirect, render httpResponse

        # 如果是登录页面, 直接向后运行
        if request.path_info == "/login/":
            return
        # 除了 /login/ 以外的其他的页面, 都需要做验证。
        # 使用 session 中的数据判断 用户是否登录
        info_dict = request.session.get("info")
        if info_dict:
            # 给 request 中赋值, 以后可以更加方便的获取session 信息
            request.info_dict = info_dict
            return

        return redirect("/login")

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

其他框架中间件阻止的问题, 访问 静态文件可能会出现。

Django深入_django_40

10.母版继承

许多的 html 文件中有相同的 内容, 可以将相同的内容提取出来,写一个母版 html 文件 , 需要这个内容的html 文件可以 extends 这个母版文件,然后,和母版html 不同的地方,在 母版html 文件中使用 {% block 内容%}

{% endblock %} 占位, 子模版可以根据自己的需求在block 中写自己的内容。

Django深入_django_41

11.ORM连表操作

员工表 中的 depart_id 字段 受到 department 表的id外键约束 在 orm 中可以使用 foreignkey 来设置外键约束。

orm 类中 用户表 的字段名叫做 depart , 数据库表中生成的字段名叫做 depart_id.

Django深入_html_42

新增user 对象时, 可以 直接传递 depart_id = 1 参数 或者传递 depart = 一个部门对象。

Django深入_中间件_43

Django深入_django_44

12.Form组件

django中的form 组件的2大作用

  • 生成HTML 表单标签
  • 数据校验

使用django 中的 forms 组件生成标签,设置默认值

Django深入_html_45

Django深入_中间件_46

Django深入_html_47

设置默认值,根据 forms 组件创建一个对象,对象中可以使用 initial 参数设置默认值, password 比较特殊,需要在 forms 类中设置 render_value =True

Django深入_django_48

Django深入_中间件_49

forms 组件数据校验

使用 forms 类创建一个对象,将form 表单中的数据 request.POST 传递给类, 使用 form.is_valid() 对数据进行校验。

Django深入_html_50

对表单进行验证,同时将表单验证失败的错误信息显示到页面上,可以取每个 form 字段的 errors 的第一个

例如 : {{form.user.errors.0}}

Django深入_html_51

Django深入_html_52

django forms 组件中的错误信息默认是 英文的, 如果要显示中文, 需要修改 settings.py 中的 LANGUAGE_CODE = "zh-hans", 如下图。

Django深入_django_53

forms 组件字段自定义验证规则, 使用正则表达式

form django.core.validators import RegexValidator

Django深入_django_54

13.登录案例

视图函数的代码

Django深入_django_55

14.ModelForm

Django深入_django_56

Django深入_django_57

Django深入_html_58

下图中的form.title.lable 和 modelform 对应的 models 中的 verbose_name 对应。

Django深入_django_59

Django深入_html_60

Django深入_中间件_61

15.django 实战

号码管理系统

1.表结构的设计

Django深入_中间件_62

Django深入_中间件_63

一共有三个数据表, 一个号码表, 一个 管理员表,一个部门表, 其中 号码表的 admin 字段和 管理员表存在外键关系, 管理员表中的 depart_id 和 部门表存在外键关系。

2. 创建项目

使用 pycharm 创建一个django 项目,

在终端使用 python manage.py startapp app名称, 创建app, 这里 为了方便使用 sqllite数据库

3. 创建相关的表结构

# app/models.py

from django.db import models

# Create your models here.


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


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

    # 增加一个年龄字段, 创建管理员的时候,不填的话, 默认为空
    age = models.IntegerField(verbose_name="年龄", null=True, blank=True)
    gender = models.CharField(verbose_name="性别",
                              choices=[(1, "男"), (2, "女")])
    depart = models.ForeignKey(verbose_name="部门", to="Department", on_delete=models.CASCADE)


class Phone(models.Model):
    """ 号码表 """
    mobile = models.CharField(verbose_name="手机号", max_length=11)
    price = models.PositiveIntegerField(verbose_name="价格", default=0)

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

    status_choices = [
        (1, "已使用"),
        (2, "未使用"),
    ]
    status = models.SmallIntegerField(verbose_name="状态", choices=status_choices, default=2)
    admin = models.ForeignKey(verbose_name="管理员", to="Admin", on_delete=models.CASCADE)

在项目终端, 使用 python manage.py makemigrations 和 python manage.py migrate 将相关的类模型 映射到数据库

4. 用户登录

  • 基本逻辑
  • 密码 + 密文存储
  • 图片验证码

Django深入_django_64

app 下 创建 static 文件夹及其子文件夹,不同类型的代码放到不同的位置

Django深入_html_65

登录前端页面实现

<!--login.html-->

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="{% static "plugins/jquery/jquery.3.6.min.js" %}">
    <link rel="stylesheet" href="{% static "plugins/bootstrap-5.3.6-dist/css/bootstrap.min.css" %}">
    <script src="{% static "plugins/bootstrap-5.3.6-dist/js/bootstrap.min.js" %}"></script>
    <style>
        .login-box{
            width:400px;
            height: 280px;
            margin-left:auto;
            margin-right:auto;
            border:1px solid #dddddd;
            margin-top:150px;
            padding-left: 20px;
            padding-right: 20px;
        }
        .login-box h2{
            text-align: center;
        }
    </style>
</head>
<body>
    <div class="login-box">
        <h2>用户登录</h2>

        <form method="post" action="/login/" novalidate>
            {% csrf_token %}
          <div class="form-group">
              <label>{{ form.username.label }}</label>
              {{ form.username }}
              <span style="color: red;">{{ form.username.errors.0 }}</span>
          </div>
          <div class="form-group">
            <label>{{ form.password.label }}</label>
              {{ form.password }}
              <span style="color:red;">{{ form.password.errors.0 }}</span>
          </div>
          <div class="form-group">
            <label>{{ form.code.label }}</label>
            <div class="row">
                <div class="col-sm-6">
                    {{ form.code }}
                </div>
                <div class="col-sm-6">
                    <img src="xxx" alt="">
                </div>
            </div>

              <span style="color:red;">{{ form.code.errors.0 }}</span>
          </div>
          <button type="submit" class="btn btn-primary">提交</button>
            <span style="color:red;">{{ error }}</span>
       </form>
    </div>
</body>
</html>
# views.py

from django.shortcuts import render
from django import forms
from django.core.validators import RegexValidator


# Create your views here.

class LoginForm(forms.Form):
    username = forms.CharField(
        label="用户名",
        widget=forms.TextInput(attrs={"class":"form-control", "placeholder":"输入用户名"}),
        # validators=[RegexValidator(r'\w{6,}$', '用户名格式错误')]
    )
    password = forms.CharField(
        label="密码",
        widget=forms.PasswordInput(attrs={"class":"form-control", "placeholder":"输入密码"}),
    )
    code = forms.CharField(
        label="验证码",
        widget=forms.TextInput(attrs={"class":"form-control", "placeholder":"输入验证码"}),
    )


def login(request):
    """ 用户登录"""
    form = LoginForm()

    return render(request, "login.html", {"form": form})
# urls.py

from django.contrib import admin
from django.urls import path
from number_management import views

urlpatterns = [
    # path('admin/', admin.site.urls),
    path("login/", views.login, name="login"),
]

5.展示图片验证码

在 项目目录下创建一个 utils 文件夹, 在下面创建一个 check_code.py 用来存放生成图片验证码的脚本,

# 生成图片验证码的脚本, 返回一个 图片对象和一个 图片中文本的字符串, 其中使用到了 pillow 模块
# pip install pillow

from PIL import Image, ImageDraw, ImageFont
import string
import random


class CaptchaGenerator:
    def __init__(self, width=100, height=30, font_size=20):
        """
        初始化验证码生成器。


        Args:
        - width (int): 图片宽度,默认为250像素。
        - height (int): 图片高度,默认为60像素。
        - font_size (int): 字体大小,默认为48像素。
        """
        self.width = width
        self.height = height
        self.font_size = font_size
        self.chars = string.ascii_letters + string.digits  # 验证码字符集合
        self.bgcolor = (255, 255, 255)  # 图片背景颜色
        self.linecolor = (random.randint(0, 128), random.randint(0, 128), random.randint(0, 128))  # 干扰线颜色
        self.dotcolor = (random.randint(0, 128), random.randint(0, 128), random.randint(0, 128))  # 干扰点颜色
        self.fontcolor = (random.randint(0, 128), random.randint(0, 128), random.randint(0, 128))  # 字体颜色


    def generate_captcha(self):
        """
        生成验证码图片和文本。


        Returns:
        - image (PIL.Image.Image): 生成的验证码图片对象。
        - captcha_text (str): 生成的验证码文本。
        """
        captcha_text = ''.join(random.choice(self.chars) for _ in range(4))  # 随机生成验证码文本
        image = Image.new('RGB', (self.width, self.height), self.bgcolor)  # 创建RGB模式的空白图片
        draw = ImageDraw.Draw(image)  # 创建可在图像上绘图的对象


        font = ImageFont.load_default().font_variant(size=self.font_size)  # 加载默认字体并调整大小


        # 绘制干扰线
        for i in range(5):
            x1 = random.randint(0, self.width)
            y1 = random.randint(0, self.height)
            x2 = random.randint(0, self.width)
            y2 = random.randint(0, self.height)
            draw.line((x1, y1, x2, y2), fill=self.linecolor, width=3)


        # 绘制干扰点
        for i in range(200):
            x = random.randint(0, self.width)
            y = random.randint(0, self.height)
            draw.point((x, y), fill=self.dotcolor)


        # 绘制验证码文字,包括阴影效果
        for i, char in enumerate(captcha_text):
            shadow_offset = random.randint(0, 3)
            shadow_color = (0, 0, 0)
            draw.text((20 + i * 20 + shadow_offset, 5 + shadow_offset), char, font=font, fill=shadow_color)  # 绘制阴影效果的文字
            draw.text((20 + i * 20, 5), char, font=font, fill=self.fontcolor)  # 绘制文字


        return image, captcha_text

修改 登录前端代码 图片验证码的位置 img标签的 src 为 "/img/code/"

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="{% static "plugins/jquery/jquery.3.6.min.js" %}">
    <link rel="stylesheet" href="{% static "plugins/bootstrap-5.3.6-dist/css/bootstrap.min.css" %}">
    <script src="{% static "plugins/bootstrap-5.3.6-dist/js/bootstrap.min.js" %}"></script>
    <style>
        .login-box{
            width:400px;
            height: 280px;
            margin-left:auto;
            margin-right:auto;
            border:1px solid #dddddd;
            margin-top:150px;
            padding-left: 20px;
            padding-right: 20px;
        }
        .login-box h2{
            text-align: center;
        }
    </style>
</head>
<body>
    <div class="login-box">
        <h2>用户登录</h2>

        <form method="post" action="/login/" novalidate>
            {% csrf_token %}
          <div class="form-group">
              <label>{{ form.username.label }}</label>
              {{ form.username }}
              <span style="color: red;">{{ form.username.errors.0 }}</span>
          </div>
          <div class="form-group">
            <label>{{ form.password.label }}</label>
              {{ form.password }}
              <span style="color:red;">{{ form.password.errors.0 }}</span>
          </div>
          <div class="form-group">
            <label>{{ form.code.label }}</label>
            <div class="row">
                <div class="col-sm-6">
                    {{ form.code }}
                </div>
                <div class="col-sm-6">
                    
                    <!--修改了下面这里-->
                    <img src="/img/code/" alt="">
                    
                </div>
            </div>

              <span style="color:red;">{{ form.code.errors.0 }}</span>
          </div>
          <button type="submit" class="btn btn-primary">提交</button>
            <span style="color:red;">{{ error }}</span>
       </form>
    </div>
</body>
</html>

urls.py 中 书写 一个视图函数和url 的映射关系

from django.contrib import admin
from django.urls import path
from number_management import views

urlpatterns = [
    # path('admin/', admin.site.urls),
    path("login/", views.login, name="login"),
    path("img/code/", views.img_code, name="img_code")
]

views.py 中写 生成验证码图片,并返回的 视图函数

from django.shortcuts import render, HttpResponse
from utils.check_code import CaptchaGenerator
from io import BytesIO


def img_code(request):
    # 生成图片
    obj = CaptchaGenerator()
    image_obj, code_str = obj.generate_captcha()
    # 图片内容写入内存, 从内存中读取并返回
    print(code_str)
    stream = BytesIO()
    image_obj.save(stream,'png')
    return HttpResponse(stream.getvalue())

效果图片

Django深入_html_66

6.点击更换图片

<div class="col-sm-6">
    <img src="/img/code/" id="codeImage">
</div>

<!--验证码 img标签设置一个 id-->

<!--利用 jquery id 选择器 获取这个标签, 给这个标签绑定一个点击事件,首先要引入jquery-->

<script src="{% static "plugins/jquery/jquery.3.6.min.js" %}"></script>
<script>
    $(function(){
        $("#codeImage").click(function(){
            // 获取img 标签的 src 属性;
            var url = $(this).attr('src');
            // 设置属性值不同于之前的,相当于向 url 发送了新的请求, 获取了新的图片验证码
            $(this).attr('src', url + "?");
        });
    })

</script>

效果, 点击图片, 会更换图片验证码

Django深入_django_67

7.使用session 保存验证码

更改 获取图片验证码的视图函数,将验证码内容写入到 session 中,方便后面登录时验证码的校验。

def img_code(request):
    # 1.生成图片
    obj = CaptchaGenerator()
    image_obj, code_str = obj.generate_captcha()

    # 2.图片内容写入内存, 从内存中读取并返回
    print(code_str)
    stream = BytesIO()
    image_obj.save(stream,'png')

    # 3.图片的内容写入session 中, 设置 60s 超时
    request.session['image_code'] = code_str
    request.session.set_expiry(60)
    
    return HttpResponse(stream.getvalue())

8.用户登录逻辑校验

用户密码存储在数据库中不能使用明文,需要加密, 可以使用 hashlib 的 md5 加密存储到数据库,

进行登录校验的时候,也需要将 密码进行 md5 加密和 存储在数据库中的加密密码进行对比。

在 utils 文件夹下 创建一个 encrypt.py 文件,在其中写 md5 加密的函数

import hashlib
from django.conf import settings


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

加密的 key 使用 django settings.py 中的 SECRET_KEY

Django深入_html_68

SECRET_KEY = 'django-insecure-0xmex#*uf-^dzkh1d82_g*3%puk#!%nugq*i1-+u5)yenpy+(6'
# views.py
from django.shortcuts import render, HttpResponse, redirect
from django import forms
from django.core.validators import RegexValidator
from utils.check_code import CaptchaGenerator
from io import BytesIO
from number_management import models
from utils.encrypt import md5

# Create your views here.

class LoginForm(forms.Form):
    username = forms.CharField(
        label="用户名",
        widget=forms.TextInput(attrs={"class":"form-control", "placeholder":"输入用户名"}),
        # validators=[RegexValidator(r'\w{6,}$', '用户名格式错误')]
    )
    password = forms.CharField(
        label="密码",
        # 设置 render_value=True , 输入验证码错误的时候,保留之前输入的密码
        widget=forms.PasswordInput(attrs={"class":"form-control", "placeholder":"输入密码"}, render_value=True),
    )
    code = forms.CharField(
        label="验证码",
        widget=forms.TextInput(attrs={"class":"form-control", "placeholder":"输入验证码"}),
    )


def login(request):
    """ 用户登录"""
    if request.method == "GET":
        form = LoginForm()
        return render(request, "login.html", {"form": form})
    form = LoginForm(data=request.POST)
    if not form.is_valid():
        return render(request, "login.html", {"form": form})

    # 判断验证码是否正确
    image_code = request.session.get("image_code")
    if not image_code:
        form.add_error("code", "验证码已过期")
        return render(request, "login.html", {"form": form})
    if image_code.upper() != form.cleaned_data['code'].upper():
        form.add_error("code", "验证码错误")
        return render(request, "login.html", {"form": form})

    # 验证码正确, 去数据库校验用户名和密码
    user = form.cleaned_data['username']

    # 密码不能够存储明文的, 可以使用 hashlib 中 的 md5 进行加密
    pwd = form.cleaned_data['password']

    encrypt_password = md5(pwd)
    print(user, encrypt_password)
    admin_object=models.Admin.objects.filter(username=user,password=encrypt_password).first()
    if not admin_object:
        return render(request, "login.html", {"form": form, "error":"用户名或密码错误"})

    request.session['info'] = {"id": admin_object.id, "name": admin_object.username}
    request.session.set_expiry(60 * 60 * 24 * 7)

    return redirect("/home")

def img_code(request):
    # 1.生成图片
    obj = CaptchaGenerator()
    image_obj, code_str = obj.generate_captcha()

    # 2.图片内容写入内存, 从内存中读取并返回
    print(code_str)
    stream = BytesIO()
    image_obj.save(stream,'png')

    # 3.图片的内容写入session 中, 设置 60s 超时
    request.session['image_code'] = code_str
    request.session.set_expiry(60)
    return HttpResponse(stream.getvalue())

6.基于中间件进行用户校验

在项目的 utils 文件夹中写一个middleware.py 写自定义的中间件,用来做用户校验

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import redirect


class AuthMiddleware(MiddlewareMixin):

    def process_request(self, request):
        # 1. 不用登录就可以访问处理
        if request.path_info in ["/login/", "/img/code/"]:
            # return 为空代码继续向后走
            return
        # 2. 获取 session
        info_dict = request.session.get("info")

        # 未登录, 重定向到 login 页面
        if not info_dict:
            return redirect("login")

        # 已登录, 将登录信息储存到 request 中
        request.info_dict = info_dict

Django深入_html_69

7.Home页面展示

写一个layout.html 作为母版,母版中写一些公共的内容,例如导航条,

导航条相关的代码从 bootstrp 官方文档中获取,修改成自己需要的样子。

注意 bootstrap依赖于 jquery , 所以也需要引入 jquery,

使用 block 进行占位

<!--layout.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-dist/css/bootstrap.css' %}">
    {% block css %}
    	
    {% endblock %}
</head>
<body>
<nav class="navbar navbar-default" style="border-radius:0;">
  <div class="container">
    <!-- Brand and toggle get grouped for better mobile display -->
    <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>

    <!-- Collect the nav links, forms, and other content for toggling -->
    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
      <ul class="nav navbar-nav">
        <li><a href="#">用户管理</a></li>
        <li><a href="#">号码管理</a></li>
      </ul>
      <ul class="nav navbar-nav navbar-right">
        <li class="dropdown">
          <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ request.info_dict.name }} <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><!-- /.navbar-collapse -->
  </div><!-- /.container-fluid -->
</nav>

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

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

home 页面继承 layout.html (模版继承)

{% extends 'layout.html' %}

{% block content %}
	<h3>欢迎{{ request.info_dict.name }}登录号码管理系统</h3>
{% endblock %}

8.注销

修改 layout.html 中的 注销 a 标签的 href 属性,点击 a 标签向 退出登录视图函数发送请求。

<ul class="dropdown-menu">
            <li><a href="#">个人资料</a></li>
            <li><a href="#">修改密码</a></li>
            <li role="separator" class="divider"></li>
            <li><a href="/logout">注销</a></li>
          </ul>

在 app 中的 views.py 中写一个 退出登录的视图函数

# views.py 
def logout(request):
    # 将session 删除调
    request.session.clear()
    return redirect("/login/")

urls.py 中的 路由和视图函数的映射

from django.urls import path
from number_management import views

urlpatterns = [
    # path('admin/', admin.site.urls),
    path("login/", views.login, name="login"),
    path("logout/", views.logout, name="logout"),
    path("img/code/", views.img_code, name="img_code"),
    path("home", views.home, name="home")
]

代码分类, 使结构变的清晰一些。

在 相关的 app 下创建一个 views 文件夹, 根据功能的不同,将不同的 视图函数放到不同的脚本中

Django深入_html_70

导入模块时,为了使条理更加的清晰, 一般把内置模块放到 脚本最上面,第三方模块,短的在上面, 长的在下面

然后 自定义模块

Django深入_中间件_71

urls.py 中的视图函数的导入位置也要做响应的修改

from django.urls import path
from number_management.views import account

urlpatterns = [
    # path('admin/', admin.site.urls),
    path("login/", account.login, name="login"),
    path("logout/", account.logout, name="logout"),
    path("img/code/", account.img_code, name="img_code"),
    path("home", account.home, name="home")
]

9.用户列表

修改 用户管理a 标签的 href 属性,点击 a 标签会向相关的视图函数发送请求,跳转页面

<ul class="nav navbar-nav">
        <li><a href="/admin/list/">用户管理</a></li>
        <li><a href="#">号码管理</a></li>
      </ul>

在 app 下的 views文件夹下创建一个 admin.py 用来存放 号码管理员相关的视图函数

# app/views/admin.py

from django.shortcuts import render
from number_management import models


def admin_list(request):
    """
    用户列表
    :param request:
    :return:
    """
    # [obj, obj, obj]  order_by("-id")  根据id 倒序排序
    queryset = models.Admin.objects.all().order_by("-id")
    # for row in queryset:
        # 如果 models.py 该字段中定义了 choice 对应关系, 使用对象.get_字段名_display() 获取 choice 中设置的
        # 对应的值 例如 row.get_gender_dispaly 可以获取 男或者 女
        # choices=[(1, "男"), (2, "女")]

        # row.depart.title , 跨表查询
        # print(row.username, row.password, row.gender, row.get_gender_display(),row.depart.title)
        # mpx 8fe4282d77a078f5a064fff057288ee9 1 男 IT

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

将相关的管理员信息传递到 html 文件中进行渲染 admin_list.html

{% extends "layout.html"  %}


{% block content %}
	<table class="table table-bordered">
        <thead>
            <tr>
                <th>ID</th>
                <th>name</th>
                <th>gender</th>
                <th>age</th>
                <th>department</th>
            </tr>
        </thead>
        <tbody>
            {% for row in queryset %}
                <tr>
                    <td>{{ row.id }}</td>
                    <td>{{ row.username }}</td>
                    <td>{{ row.get_gender_display }}</td>
                    <td>{{ row.age }}</td>
                    <td>{{ row.depart.title}}</td>
                </tr>
            {% endfor %}
        </tbody>
    </table>
{% endblock %}

效果图

Django深入_django_72

10.新建用户

在admin_list.html 页面添加一个 a标签,点击跳转到 /admin/add/ 页面, 同时在a 标签的前面添加了一个 bootstrap 官网的 图标

{% extends "layout.html"  %}


{% block content %}
    <!-- 改动的地方-->
    <div style="margin-bottom:10px">
        <a class="btn btn-success" href="/admin/add/">
            <span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建用户</a>
    </div>
	<table class="table table-bordered">
        <thead>
            <tr>
                <th>ID</th>
                <th>name</th>
                <th>gender</th>
                <th>age</th>
                <th>department</th>
            </tr>
        </thead>
        <tbody>
            {% for row in queryset %}
                <tr>
                    <td>{{ row.id }}</td>
                    <td>{{ row.username }}</td>
                    <td>{{ row.get_gender_display }}</td>
                    <td>{{ row.age }}</td>
                    <td>{{ row.depart.title}}</td>
                </tr>
            {% endfor %}
        </tbody>
    </table>
{% endblock %}

Django深入_django_73

在urls.py 中写相关的视图函数和url 的映射关系

from django.urls import path
from number_management.views import account, admin

urlpatterns = [
    path("admin/add/", admin.admin_add, name="admin_add")
]

在app/views/admin.py 中写 admin_add 视图函数

from django import forms

class AdminModelForm(forms.ModelForm):
    class Meta:
        model = models.Admin
        fields = ["username", "password", "age", "gender", "depart"]


def admin_add(request):
    form = AdminModelForm()
    return render(request, "admin_add.html", {"form": form})

admin_add.html

{% extends "layout.html" %}

{% block content %}
    <form action="" method="post">
        {% csrf_token %}

        {% for field in form %}
            <p>{{ field.label }}: {{ field }} <span style="color:red">{{ field.errors.0 }}</span></p>
        {% endfor %}
        <input type="submit" value="提交">
    </form>
{% endblock %}

Django深入_html_74

如上图, 使用 modelform, 和相关的model 有联系,给部门model 设置__str__,显示 __str__ 中的内容。显示每个对象的title

和choice 相关的字段不设置 默认 初始会显示一系列的横杆,设置 default, 显示设置的默认值,如上图,设置default= 1, 默认显示1

admin_add效果图

Django深入_html_75

11.表单的展示

上面的表单不好看, 利用 bootstrp 中的模版,展示一个好看的表单。

面板中套了一个 form 表单(bootstrap 上的模版)

{% extends "layout.html" %}

{% block content %}
    <div class="panel panel-default">
      <div class="panel-heading"><span class="glyphicon glyphicon-indent-left" aria-hidden="true"></span>表单操作</div>
      <div class="panel-body">
          <form class="form-horizontal">
              {% csrf_token %}
              {% for field in form %}
                  <div class="form-group">
                      <label for="inputEmail3" class="col-sm-2 control-label">{{ field.label }}</label>
                      <div class="col-sm-10">
                          {{ field }}
                          <span style="color:red">{{ field.errors.0 }}</span>
                      </div>
                </div>
              {% endfor %}

              <div class="form-group">
                  <div class="col-sm-offset-2 col-sm-10">
                      <button type="submit" class="btn btn-primary">保 存</button>
                  </div>
              </div>
          </form>
      </div>
    </div>
{% endblock %}

Django深入_html_76

还是不好看,可以给添加一个 form-control, 可以 使用两种方式

app\views\admin.py

from django.shortcuts import render
from number_management import models
from django import forms


def admin_list(request):
    """
    用户列表
    :param request:
    :return:
    """
    # [obj, obj, obj]  order_by("-id")  根据id 倒序排序
    queryset = models.Admin.objects.all().order_by("-id")
    # for row in queryset:
        # 如果 models.py 该字段中定义了 choice 对应关系, 使用对象.get_字段名_display() 获取 choice 中设置的
        # 对应的值 例如 row.get_gender_dispaly 可以获取 男或者 女
        # choices=[(1, "男"), (2, "女")]

        # row.depart.title , 跨表查询
        # print(row.username, row.password, row.gender, row.get_gender_display(),row.depart.title)
        # mpx 8fe4282d77a078f5a064fff057288ee9 1 男 IT

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


class AdminModelForm(forms.ModelForm):
    class Meta:
        model = models.Admin
        fields = ["username", "password", "age", "gender", "depart"]
        # 给表单增加样式, 字段比较多,加起来有点麻烦
        # widgets ={
        #     'username': forms.TextInput(attrs={"class": "form-control"})
        # }

    # 如果类中没有 __init__ 方法,执行父类中的__init__ 方法, 如果有就执行自己的__init__ 方法
    def __init__(self, *args, **kwargs):
        # 先执行一下父类的init 方法
        super().__init__(*args, **kwargs)

        # 自定义一些操作, 找到所有的字段
        print("----")
        print(self.fields)
        for name, field_object in self.fields.items():
            print(name, field_object)
            field_object.widget.attrs = {"class": "form-control"}


def admin_add(request):
    form = AdminModelForm()
    return render(request, "admin_add.html", {"form": form})

Django深入_html_77

输入框占一整行也不太好看, 给每个 输入框加一个 row, row 具有统一的边距, 每个form_group, 放到一个div 中,占 整行的6份, 一共12份, 给div 加一个 class="col-xs-6"

{% extends "layout.html" %}

{% block content %}
    <div class="panel panel-default">
      <div class="panel-heading"><span class="glyphicon glyphicon-indent-left" aria-hidden="true"></span>表单操作</div>
      <div class="panel-body">
          <form class="form-horizontal">
              {% csrf_token %}
              <div class="row">
                 {% for field in form %}
                     <div class="col-xs-6">
                         <div class="form-group">
                      <label for="inputEmail3" class="col-sm-2 control-label">{{ field.label }}</label>
                      <div class="col-sm-10">
                          {{ field }}
                          <span style="color:red">{{ field.errors.0 }}</span>
                      </div>
                </div>
                     </div>
                 {% endfor %}
              </div>
              <div class="row">
                  <div class="form-group">
                  <div class="col-xs-6">
                      <div class="col-sm-offset-2 col-sm-10">
                      <button type="submit" class="btn btn-primary">保 存</button>
                  </div>
                  </div>
              </div>
              </div>
          </form>
      </div>
    </div>
{% endblock %}

Django深入_中间件_78

展示错误信息的时候,会把标签挤到下一个位置, 使用 positon:relative 和 position: absolute

相对定位

Django深入_html_79

{% extends "layout.html" %}

{% block content %}
    <div class="panel panel-default">
      <div class="panel-heading"><span class="glyphicon glyphicon-indent-left" aria-hidden="true"></span>表单操作</div>
      <div class="panel-body">
          <form class="form-horizontal">
              {% csrf_token %}
              <div class="row">
                 {% for field in form %}
                     <div class="col-xs-6">
                         <!-- 加一个底部边距可以使 错误信息离输入框远一点-->
                         <div class="form-group" style="margin-bottom:25px;">
                             <label class="col-sm-2 control-label">{{ field.label }}</label>
                             <div class="col-sm-10" style="position:relative">
                                 {{ field }}
                          <span style="color:red; position:absolute">{{ field.errors.0 }}</span>
                      </div>
                </div>
                     </div>
                 {% endfor %}
              </div>
              <div class="row">
                  <div class="form-group">
                  <div class="col-xs-6">
                      <div class="col-sm-offset-2 col-sm-10">
                      <button type="submit" class="btn btn-primary">保 存</button>
                  </div>
                  </div>
              </div>
              </div>
          </form>
      </div>
    </div>
{% endblock %}

12.新建用户成功

Django深入_html_80

form 表单指定提交方式为 post 方式 admin_add.html

{% extends "layout.html" %}

{% block content %}
    <div class="panel panel-default">
      <div class="panel-heading"><span class="glyphicon glyphicon-indent-left" aria-hidden="true"></span>表单操作</div>
      <div class="panel-body">
          <form class="form-horizontal" method="post" novalidate>
              {% csrf_token %}
              <div class="row">
                 {% for field in form %}
                     <div class="col-xs-6">
                         <div class="form-group" style="margin-bottom:25px;">
                             <label class="col-sm-2 control-label">{{ field.label }}</label>
                             <div class="col-sm-10" style="position:relative">
                                 {{ field }}
                          <span style="color:red; position:absolute">{{ field.errors.0 }}</span>
                      </div>
                </div>
                     </div>
                 {% endfor %}
              </div>
              <div class="row">
                  <div class="form-group">
                  <div class="col-xs-6">
                      <div class="col-sm-offset-2 col-sm-10">
                      <button type="submit" class="btn btn-primary">保 存</button>
                  </div>
                  </div>
              </div>
              </div>
          </form>
      </div>
    </div>
{% endblock %}

用户添加视图函数完成

app/views/admin.py

from django.shortcuts import render, redirect
from django import forms

from number_management import models
from utils.encrypt import md5

def admin_add(request):
    if request.method == "GET":
        form = AdminModelForm()
        return render(request, "admin_add.html", {"form": form})
    form = AdminModelForm(data=request.POST)
    if not form.is_valid():
        return render(request, "admin_add.html", {"form": form})

    # 读取密码并更新成md5 加密的新值
    form.instance.password = md5(form.instance.password)

    # 保存到数据库
    form.save()
    return redirect("/admin/list/")

13.编辑用户

admin_list.html 每一行的数据后面加一个 编辑a标签,点击这个标签,跳转到 admin_edit.html

admin_add.html 和 admin_edit.html 页面的效果是一样的, 可以将 admin_add.html 更改成 admin_form.html, admin_add 和 admin_edit 操作都使用这个页面。

编辑的时候,只能编辑 密码以外的信息, 这个时候, 可以 把 adminModelForm 复制一份,删除其中的

password 字段

class AdminEditModelForm(forms.ModelForm):
    class Meta:
        model = models.Admin
        fields = ["username","age", "gender", "depart"]
        # 给表单增加样式, 字段比较多,加起来有点麻烦
        # widgets ={
        #     'username': forms.TextInput(attrs={"class": "form-control"})
        # }

    # 如果类中没有 __init__ 方法,执行父类中的__init__ 方法, 如果有就执行自己的__init__ 方法
    def __init__(self, *args, **kwargs):
        # 先执行一下父类的init 方法
        super().__init__(*args, **kwargs)

        # 自定义一些操作, 找到所有的字段
        print("----")
        print(self.fields)
        for name, field_object in self.fields.items():
            print(name, field_object)
            field_object.widget.attrs = {"class": "form-control"}

编辑页面应该有默认值, admin_edit 视图函数是和 参数拼接到路径中的 url 对应的, 点击 admin_list 相关行的编辑a标签时,会把相关的 用户 id 传递给视图函数, 使用这个 id, 可以从数据库中查找到相关的信息,使用

# 编辑之前应该有默认值
    admin_object = models.Admin.objects.filter(id=aid).first()
    form = AdminEditModelForm(instance=admin_object)

如上两行指定 form 表单的默认值

from django.contrib import admin
from django.urls import path
from number_management.views import account, admin

urlpatterns = [
    # path('admin/', admin.site.urls),
    path("login/", account.login, name="login"),
    path("logout/", account.logout, name="logout"),
    path("img/code/", account.img_code, name="img_code"),
    path("home", account.home, name="home"),

    path("admin/list/", admin.admin_list, name="admin_list"),
    path("admin/add/", admin.admin_add, name="admin_add"),
    
    // 参数拼接到路径中
    path("admin/edit/<int:aid>/", admin.admin_edit, name="admin_edit")
]

编辑视图函数 app/views/admin.py 中

def admin_edit(request, aid):
    # 编辑之前应该有默认值
    admin_object = models.Admin.objects.filter(id=aid).first()
    if request.method == "GET":
        form = AdminEditModelForm(instance=admin_object)
        return render(request, "admin_form.html", {"form": form})
    form = AdminEditModelForm(instance=admin_object, data=request.POST)
    if not form.is_valid():
        return render(request, 'admin_form.html', {"form":form})

    # 更新
    form.save()
    return redirect("/admin/list/")

14.删除用户

删除时,使用bootstrap 中的 模态框,点击删除弹出一个模态框, 确认是否删除

Django深入_django_81

<!-- Button trigger modal -->
<button type="button" class="btn btn-primary btn-lg" data-toggle="modal" data-target="#myModal">
  Launch demo modal
</button>

<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
        <h4 class="modal-title" id="myModalLabel">Modal title</h4>
      </div>
      <div class="modal-body">
        ...
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
        <button type="button" class="btn btn-primary">Save changes</button>
      </div>
    </div>
  </div>
</div>

Django深入_中间件_82

删除时比较危险的操作, 可以使用 警告框的样式。

Django深入_django_83

检查-->定位-->copy 样式,

Django深入_中间件_84

<!--使用 class 中的内容  class="alert alert-danger alert-dismissible fade in" -->

<div class="alert alert-danger alert-dismissible fade in" role="alert">
      <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
      <h4>Oh snap! You got an error!</h4>
      <p>Change this and that and try again. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Cras mattis consectetur purus sit amet fermentum.</p>
      <p>
        <button type="button" class="btn btn-danger">Take this action</button>
        <button type="button" class="btn btn-default">Or do this</button>
      </p>
    </div>

admin_list.html

{% extends "layout.html" %}


{% block content %}

    <div style="margin-bottom:10px">
        <a class="btn btn-success" href="/admin/add/">
            <span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建用户</a>
    </div>
    <table class="table table-bordered">
        <thead>
        <tr>
            <th>ID</th>
            <th>name</th>
            <th>gender</th>
            <th>age</th>
            <th>department</th>
            <th>操作</th>
        </tr>
        </thead>
        <tbody>
        {% for row in queryset %}
            <tr>
                <td>{{ row.id }}</td>
                <td>{{ row.username }}</td>
                <td>{{ row.get_gender_display }}</td>
                <td>{{ row.age }}</td>
                <td>{{ row.depart.title }}</td>
                <td>
                    <a class="btn btn-primary btn-xs" href="/admin/edit/{{ row.id }}/">
                        <!--图标-->
                        {#                            <span class="glyphicon glyphicon-edit" aria-hidden="true"></span>#}
                        编辑
                    </a>
                    <input type="button" value="删除" class="btn btn-danger btn-xs" data-toggle="modal"
                           data-target="#myModal">
                </td>
            </tr>
        {% endfor %}
        </tbody>
    </table>

    <!-- Modal -->
    <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
        <div class="modal-dialog" role="document">

            <div class="alert alert-danger alert-dismissible fade in" role="alert">
                <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span
                        aria-hidden="true">×</span></button>
                <h4>Oh snap! You got an error!</h4>
                <p>Change this and that and try again. Duis mollis, est non commodo luctus, nisi erat porttitor
                    ligula, eget lacinia odio sem nec elit. Cras mattis consectetur purus sit amet fermentum.</p>
                <p>
                    <button type="button" class="btn btn-danger">Take this action</button>
                    <button type="button" class="btn btn-default">Or do this</button>
                </p>
            </div>
        </div>
    </div>
{% endblock %}

Django深入_django_85

{% extends "layout.html" %}


{% block content %}

    <div style="margin-bottom:10px">
        <a class="btn btn-success" href="/admin/add/">
            <span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建用户</a>
    </div>
    <table class="table table-bordered">
        <thead>
        <tr>
            <th>ID</th>
            <th>name</th>
            <th>gender</th>
            <th>age</th>
            <th>department</th>
            <th>操作</th>
        </tr>
        </thead>
        <tbody>
        {% for row in queryset %}
            <tr>
                <td>{{ row.id }}</td>
                <td>{{ row.username }}</td>
                <td>{{ row.get_gender_display }}</td>
                <td>{{ row.age }}</td>
                <td>{{ row.depart.title }}</td>
                <td>
                    <a class="btn btn-primary btn-xs" href="/admin/edit/{{ row.id }}/">
                        <!--图标-->
                        {#                            <span class="glyphicon glyphicon-edit" aria-hidden="true"></span>#}
                        编辑
                    </a>
                    <input type="button" value="删除" class="btn btn-danger btn-xs" data-toggle="modal"
                           data-target="#myModal">
                </td>
            </tr>
        {% endfor %}
        </tbody>
    </table>

    <!-- Modal -->
    <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
        <div class="modal-dialog" role="document">

            <div class="alert alert-danger alert-dismissible fade in" role="alert">
                <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span
                        aria-hidden="true">×</span></button>
                <h4>是否确认删除?</h4>
                <p style="margin: 20px 0;">删除后,与之关联的所有相关的数据都会被删除调,确认是否继续?</p>
                <p>
                    <button type="button" class="btn btn-danger">确 认</button>
                    <button type="button" class="btn btn-default">取 消</button>
                </p>
            </div>
        </div>
    </div>
{% endblock %}

Django深入_html_86

点击删除按钮, 获取到需要删除的行数据,给按钮绑定相应的点击事件,点击事件触发 模态框弹出

模态框的弹出,可以使用 类属性的方式,也可以使用 javascript 调用的方式

Django深入_django_87

点击 删除按钮,设置,全局删除ID 为 需要删除的 id ,同时弹出模态框

点击 模态框中的确认时, 将需要删除的id 传递给后端的视图函数, 删除数据,重定向到 admin_list.html 页面

点击模态框中的 取消按钮时, 将模态框设置隐藏。

需要给 模态框中的 两个按钮(确认和取消, 绑定 onclick 事件)

{% block js %}
    <script>
        // 当前删除的 id
        DELETE_ID = 0;
        function deleteAction(id){
            DELETE_ID = id;
            console.log(id);
            $("#myModel").modal("show");
        }
        
    </script>
{% endblock %}
{% block js %}
    <script>
        // 当前删除的 id
        DELETE_ID = 0;
        function deleteAction(id){
            DELETE_ID = id;
            console.log(id);
            $("#myModal").modal("show");
        }
        // 确认删除
        function confirmDelete(){
            // 向后端发送请求,  DELETE_ID -> 删除
            // 页面刷新
        }

        //取消删除
        function cancelDelete(){
            // 全局变量重置成0, 模态框设置隐藏
            DELETE_ID = 0;
            $("#myModal").modal("hide");
        }

    </script>
{% endblock %}

向后端发送请求的方式,

- 点击 a标签, 拼接参数的方式
- post 请求, submit 提交的时候

上面这两种,发送请求的时候, 后刷新页面

ajax 发送请求发送请求的时候, 不会刷新页面

Django深入_html_88

点击模态框的中的确认删除 按钮时, 向后端发送 ajax 请求。

Django深入_html_89

{% block js %}
    <script>
        // 当前删除的 id
        DELETE_ID = 0;
        function deleteAction(id){
            DELETE_ID = id;
            console.log(id);
            $("#myModal").modal("show");
        }
        // 确认删除
        function confirmDelete(){
            // 向后端发送请求,  DELETE_ID -> 删除
            // 页面刷新
            // admin/delete?aid=1
            $.ajax({
                url:"admin/delete/",
                type:"GET",
                data:{
                    aid:DELETE_ID
                },
                success:function(res){
                    console.log("请求返回值:", res);
            }
            })
        }

        //取消删除
        function cancelDelete(){
            // 全局变量重置成0, 模态框设置隐藏
            DELETE_ID = 0;
            $("#myModal").modal("hide");
        }

    </script>
{% endblock %}

urls.py 中的 url 和视图函数的对应关系

from django.contrib import admin
from django.urls import path
from number_management.views import account, admin

urlpatterns = [
    # path('admin/', admin.site.urls),
    path("login/", account.login, name="login"),
    path("logout/", account.logout, name="logout"),
    path("img/code/", account.img_code, name="img_code"),
    path("home", account.home, name="home"),

    path("admin/list/", admin.admin_list, name="admin_list"),
    path("admin/add/", admin.admin_add, name="admin_add"),
    path("admin/edit/<int:aid>/", admin.admin_edit, name="admin_edit"),
    
    # 新增的 url
    path("admin/delete/<int:aid>/", admin.admin_delete, name="admin_delete")
]

app/views/admin.py 中的 admin_delete 视图函数

from django.shortcuts import render, redirect, HttpResponse
from django import forms
from django.http import JsonResponse

from number_management import models
from utils.encrypt import md5

def admin_delete(request):
    aid = request.GET.get("aid")
    print("要删除的ID:", aid)
    models.Admin.objects.filter(id=aid).delete()
    # return HttpResponse("11")
    return JsonResponse({"status": True})

admin_list.html

{% extends "layout.html" %}


{% block content %}

    <div style="margin-bottom:10px">
        <a class="btn btn-success" href="/admin/add/">
            <span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建用户</a>
    </div>
    <table class="table table-bordered">
        <thead>
        <tr>
            <th>ID</th>
            <th>name</th>
            <th>gender</th>
            <th>age</th>
            <th>department</th>
            <th>操作</th>
        </tr>
        </thead>
        <tbody>
        {% for row in queryset %}
            <tr>
                <td>{{ row.id }}</td>
                <td>{{ row.username }}</td>
                <td>{{ row.get_gender_display }}</td>
                <td>{{ row.age }}</td>
                <td>{{ row.depart.title }}</td>
                <td>
                    <a class="btn btn-primary btn-xs" href="/admin/edit/{{ row.id }}/">
                        <!--图标-->
                        {#                            <span class="glyphicon glyphicon-edit" aria-hidden="true"></span>#}
                        编辑
                    </a>
                    <input type="button" value="删除" class="btn btn-danger btn-xs" onclick="deleteAction({{ row.id }});">
                </td>
            </tr>
        {% endfor %}
        </tbody>
    </table>

    <!-- Modal -->
    <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
        <div class="modal-dialog" role="document">

            <div class="alert alert-danger alert-dismissible fade in" role="alert">
                <button type="button" class="close" data-dismiss="alert" aria-label="Close"></button>
                <h4>是否确认删除?</h4>
                <p style="margin: 20px 0;">删除后,与之关联的所有相关的数据都会被删除调,确认是否继续?</p>
                <p>
                    <button type="button" class="btn btn-danger" onclick="confirmDelete()">确 认</button>
                    <button type="button" class="btn btn-default" onclick="cancelDelete()">取 消</button>
                </p>
            </div>
        </div>
    </div>
{% endblock %}

{% block js %}
    <script>
        // 当前删除的 id
        DELETE_ID = 0;
        function deleteAction(id){
            DELETE_ID = id;
            console.log(id);
            $("#myModal").modal("show");
        }
        // 确认删除
        function confirmDelete(){
            // 向后端发送请求,  DELETE_ID -> 删除
            // 页面刷新
            // admin/delete?aid=1
            $.ajax({
                url:"/admin/delete/",
                type:"GET",
                data:{
                    aid:DELETE_ID
                },
                dataType: "JSON",
                success:function(res){
                    if(res.status){
                        // 删除成功,刷新页面
                        location.reload();
                    }else{
                        alert(res.error);
                    }
                    console.log("请求返回值:", res);
            }
            })
        }

        //取消删除
        function cancelDelete(){
            // 全局变量重置成0, 模态框设置隐藏
            DELETE_ID = 0;
            $("#myModal").modal("hide");
        }

    </script>
{% endblock %}

15.号码管理平台实现思路

参考 用户管理实现

  • 号码列表展示
    点击导航条上的号码管理a 标签访问 号码管理的 url 和视图函数 ,在视图函数中从数据库中查询所有的号码展示到 号码管理页面
<ul class="nav navbar-nav">
        <li><a href="/admin/list/">用户管理</a></li>
        <li><a href="/number/list/">号码管理</a></li>
      </ul>

url 和视图的映射关系

from django.contrib import admin
from django.urls import path
from number_management.views import account, admin, number

urlpatterns = [
    # path('admin/', admin.site.urls),
    path("login/", account.login, name="login"),
    path("logout/", account.logout, name="logout"),
    path("img/code/", account.img_code, name="img_code"),
    path("home", account.home, name="home"),

    path("admin/list/", admin.admin_list, name="admin_list"),
    path("admin/add/", admin.admin_add, name="admin_add"),
    path("admin/edit/<int:aid>/", admin.admin_edit, name="admin_edit"),
    path("admin/delete/", admin.admin_delete, name="admin_delete"),
	
    # 新增的 url
    path("number/list", number.number_list, name="number_list")
]

在 app/views/文件夹下创建一个 number.py 用来存放 number 相关的视图函数

from django.shortcuts import render
from number_management import models


def number_list(request):
    query_set = models.Phone.objects.all().order_by("-id")
    return render(request, "number_list.html", {"query_set": query_set})

参考 admin_list.html 书写 number_list.html

面板中的 号码展示,

新增号码按钮, 点击访问 url /number/add

编辑号码按钮, 点击访问url /number/edit

删除号码按钮, 点击删除,使用 jquery 绑定事件弹出 模态框,点击模态框中的确认按钮,向 url number/delete 发送ajax 请求(页面不刷新)传递 需要删除的数据行的 id, 删除对应的号码

点击模态框中的取消按钮,使用 jquery 绑定事件,隐藏模态框的展示

# urls.py
from django.urls import path
from number_management.views import account, admin, number

urlpatterns = [
    # path('admin/', admin.site.urls),
    path("login/", account.login, name="login"),
    path("logout/", account.logout, name="logout"),
    path("img/code/", account.img_code, name="img_code"),
    path("home", account.home, name="home"),

    path("admin/list/", admin.admin_list, name="admin_list"),
    path("admin/add/", admin.admin_add, name="admin_add"),
    path("admin/edit/<int:aid>/", admin.admin_edit, name="admin_edit"),
    path("admin/delete/", admin.admin_delete, name="admin_delete"),

    path("number/list/", number.number_list, name="number_list"),
    path("number/add/", number.number_add, name="number_add"),
    path("number/edit/<int:aid>/", number.number_edit, name="number_edit"),
    path("number/delete/", number.number_delete, name="number_delete")
]

app/views/number.py

from django import forms
from django.shortcuts import render, redirect
from django.http import JsonResponse
from number_management import models


class numberModelForm(forms.ModelForm):
    class Meta:
        model = models.Phone
        fields = ["mobile", "price", "level", "status", "admin"]
        # 给表单增加样式, 字段比较多,加起来有点麻烦
        # widgets ={
        #     'username': forms.TextInput(attrs={"class": "form-control"})
        # }

    # 如果类中没有 __init__ 方法,执行父类中的__init__ 方法, 如果有就执行自己的__init__ 方法
    def __init__(self, *args, **kwargs):
        # 先执行一下父类的init 方法
        super().__init__(*args, **kwargs)

        # 自定义一些操作, 找到所有的字段
        print("----")
        print(self.fields)
        for name, field_object in self.fields.items():
            print(name, field_object)
            field_object.widget.attrs = {"class": "form-control"}


def number_list(request):
    query_set = models.Phone.objects.all().order_by("-id")
    return render(request, "number_list.html", {"query_set": query_set})


def number_add(request):
    if request.method == "GET":
        form = numberModelForm()
        return render(request, "number_form.html", {"form": form})
    form = numberModelForm(request.POST)
    if not form.is_valid():
        return render(request, "number_form.html",{"form": form})

    # 保存到数据库
    form.save()
    return redirect("number_list")


def number_edit(request, aid):
    edit_object = models.Phone.objects.filter(id=aid).first()
    if request.method == "GET":
        form = numberModelForm(instance=edit_object)
        return render(request, "number_form.html", {"form": form})
    form = numberModelForm(instance=edit_object, data=request.POST)
    if not form.is_valid():
        return render(request, "number_form.html", {"form": form})

    form.save()
    return redirect("number_list")


def number_delete(request):
    aid = request.GET.get("aid")
    models.Phone.objects.filter(id=aid).delete()
    return JsonResponse({"status": True})

number_form.html

{% extends "layout.html" %}

{% block content %}
    <div class="panel panel-default">
      <div class="panel-heading"><span class="glyphicon glyphicon-indent-left" aria-hidden="true"></span>表单操作</div>
      <div class="panel-body">
          <form class="form-horizontal" method="post" novalidate>
              {% csrf_token %}
              <div class="row">
                 {% for field in form %}
                     <div class="col-xs-6">
                         <div class="form-group" style="margin-bottom:25px;">
                             <label class="col-sm-2 control-label">{{ field.label }}</label>
                             <div class="col-sm-10" style="position:relative">
                                 {{ field }}
                          <span style="color:red; position:absolute">{{ field.errors.0 }}</span>
                      </div>
                </div>
                     </div>
                 {% endfor %}
              </div>
              <div class="row">
                  <div class="form-group">
                  <div class="col-xs-6">
                      <div class="col-sm-offset-2 col-sm-10">
                      <button type="submit" class="btn btn-primary">保 存</button>
                  </div>
                  </div>
              </div>
              </div>
          </form>
      </div>
    </div>
{% endblock %}

number_list.html

{% extends "layout.html" %}


{% block content %}

    <div style="margin-bottom:10px">
        <a class="btn btn-success" href="/number/add/">
            <span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建号码</a>
    </div>
    <table class="table table-bordered">
        <thead>
        <tr>
            <th>ID</th>
            <th>Phone</th>
            <th>Price</th>
            <th>Level</th>
            <th>status</th>
            <th>admin</th>
            <th>操作</th>
        </tr>
        </thead>
        <tbody>
        {% for row in query_set %}
            <tr>
                <td>{{ row.id }}</td>
                <td>{{ row.mobile }}</td>
                <td>{{ row.price }}</td>
                <td>{{ row.get_level_display }}</td>
                <td>{{ row.get_status_display }}</td>
                <td>{{ row.admin.username }}</td>
                <td>
                    <a class="btn btn-primary btn-xs" href="/number/edit/{{ row.id }}/">
                        <!--图标-->
                        {#                            <span class="glyphicon glyphicon-edit" aria-hidden="true"></span>#}
                        编辑
                    </a>
                    <input type="button" value="删除" class="btn btn-danger btn-xs" onclick="deleteAction({{ row.id }});">
                </td>
            </tr>
        {% endfor %}
        </tbody>
    </table>

    <!-- Modal -->
    <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
        <div class="modal-dialog" role="document">

            <div class="alert alert-danger alert-dismissible fade in" role="alert">
                <button type="button" class="close" data-dismiss="alert" aria-label="Close"></button>
                <h4>是否确认删除?</h4>
                <p style="margin: 20px 0;">删除后,与之关联的所有相关的数据都会被删除调,确认是否继续?</p>
                <p>
                    <button type="button" class="btn btn-danger" onclick="confirmDelete()">确 认</button>
                    <button type="button" class="btn btn-default" onclick="cancelDelete()">取 消</button>
                </p>
            </div>
        </div>
    </div>
{% endblock %}

{% block js %}
    <script>
        // 当前删除的 id
        DELETE_ID = 0;
        function deleteAction(id){
            DELETE_ID = id;
            console.log(id);
            $("#myModal").modal("show");
        }
        // 确认删除
        function confirmDelete(){
            // 向后端发送请求,  DELETE_ID -> 删除
            // 页面刷新
            // admin/delete?aid=1
            $.ajax({
                url:"/number/delete/",
                type:"GET",
                data:{
                    aid:DELETE_ID
                },
                dataType: "JSON",
                success:function(res){
                    if(res.status){
                        // 删除成功,刷新页面
                        location.reload();
                    }else{
                        alert(res.error);
                    }
                    console.log("请求返回值:", res);
            }
            })
        }

        //取消删除
        function cancelDelete(){
            // 全局变量重置成0, 模态框设置隐藏
            DELETE_ID = 0;
            $("#myModal").modal("hide");
        }

    </script>
{% endblock %}

16.总结

Django深入_中间件_90

Django深入_中间件_91