Django Silver Horn King Wu Peiqi supporting video notes, python full stack development, pythonWeb

Django

This blog is based on the Django full-stack development video of Silver Horn King Wu Peiqi:Portal
If there are any errors or improvements欢迎大家评论、私信交流指正

1. First introduction to Django

1. Djang installation

  • In the windows command window

Win+r key, enter cmd

pip install django
  • Effect:

image-20221023110511676

2. Django project

①Create Django project
  • Created using Pycham
Ⅰ. Use templates
  • On the main page, click File->newProject in the upper left corner
Ⅱ. Configuration options

image-20221023171059188

Ⅲ. Successful effect

image-20221023172109306

②Project files
Ⅰ. Introduction to default files
Demo1
├── manage.py         【项目的管理,启动项目、创建app、数据管理】【不需要修改】【***常常用***
├── templates          【存放html静态页面,后期手动创建】
├── static              【图片、css、js、plugins(插件)等前端样式资源,后期手动创建】
└── Demo1
    ├── __init__.py
    ├── settings.py    【项目配置文件】          【***常常修改***
    ├── urls.py        【URL和函数的对应关系】【***常常修改***
    ├── asgi.py        【接收网络请求】【不要动】
    └── wsgi.py        【接收网络请求】【不要动】
③Create App file
  • Note: Our development is relatively simple and does not require multiple apps. Under normal circumstances, just create one app under the project.
Ⅰ. Terminal creation
  • grammar
python manage.py startapp app文件名称
  • Execute in the current project directorymanage.pyFile creation App
Ⅱ. app file structure
Demo1
├── app01
│   ├── __init__.py
│   ├── admin.py         【固定,不用动】django默认提供了admin后台管理。
│   ├── apps.py          【固定,不用动】app启动类
│   ├── migrations       【固定,不用动】数据库变更记录
│   │   └── __init__.py
│   ├── models.py        【**重要**】,对数据库操作,里面的oim代替了原生的Pymysql来操作数据库。
│   ├── tests.py         【固定,不用动】单元测试
│   └── views.py         【最常用】,列:下面执行url就会调用该文件夹的函数。
├── manage.py
└── Demo1
    ├── __init__.py
    ├── asgi.py
    ├── settings.py      【项目配置文件】
    ├── urls.py          【URL->函数】
    └── wsgi.py

3. Get started quickly

①Project registration app
Ⅰ. In the Settings.py file under the project folder
  • app address format
'app文件名.apps.类名(apps.py文件中)'
  • Class name

image-20221023181313400

  • Fill in the information

image-20221023181016450

②urls.py writes url and function information

image-20221023182209716

③views.py writes view functions
  • Server output layer

image-20221023182612280

④Start the project
Ⅰ. Command line startup
  • The port number can be omitted, the default is 8000
python manage.py runserver 8080
  • closure
ctrl+c
Ⅱ. PyCham starts

image-20221023185327679

⑤Access page

When accessed in the browser, 127.0.0.1.8080 is the default, and index is the page to be accessed and the execution function path,See configuration for details

image-20221023190334635

4. Visit static pages

  • It’s the same as the quick start steps, only a few positions have been modified.
①Create html static page
  • Create a folder templates in the app file directory and place the html static page in it

  • Select the templates folder, right-click new—File—Html, and then take the file name user_list

  • Effect:

image-20221023190927924

②Write view function
  • views.py file in app folder

image-20221023191219600

④Write url configuration file
  • The urls.py file in the Demo1 project folder
    image-20221023171059188
⑤Access page
  • start up

  • Access effect:
    image-20221023191629221

⑥Summary
  • AccessStatic page return methodUse:render()And import the package
# 调用函数访问静态页面
def user_list(request):
    return render(request,"user_list.html")
  • Common direct useHttpResponse()Method
def index(request):
    return HttpResponse("欢迎使用Django")
⑥Expand templates
  • By default, the page looks for the html file in the templates folder of the App file directory, but it can be configured to look for it in the project directory.

  • Add method:

os.path.jion()

5. Project file management

①Static page
Ⅰ. Storage path
  • app text— templates text subject
  • The folder name must be templates and cannot be modified.
    image-20221023193229717
Ⅱ. Expansion (modify storage path)
  • The page looks for html files in the templates folder of the App file directory by default, but it can be configured to look for it in the project directory, which can be viewed
    image-20221023192552299

  • Add method

os.path.jion()
②Static files
  • In the App directory, the name must bestatic file

  • Front-end style resources such as images, css, js, and plugins
    image-20221023193824517

6. Django template syntax

①Citation documents
Django unique resource path
  • Specify the file search path through the project configuration file Settings.py, and the placeholder refers to the resource
  • effect:

To prevent the need to change the file path for future projects, you can modify it directly through the configuration file.

  • grammar:
<img src="{% 配置文件指定的路径 '文件路径' %}">
  • Effect:
<img src="{%  static 'img/img.pong' %}">
  • File location path:
    image-20221023195954741

  • Configuration file:
    image-20221023195843606

②Return data
Ⅰ. render object
  • illustrate

  • Guide package

from django.shortcuts import render
  • use
def user_list(request):
    return render(request,"user_list.html")
Ⅱ. HttpResponse object
  • illustrate

  • Guide package

from django.shortcuts import HttpResponse
  • use
def index(request):
    return HttpResponse("欢迎使用Django") # 结果输出到访问的页面
③Placeholder development
  • Use placeholders to make python code available in html files
{
    
    % python代码 %}
  • views.py file

from django.shortcuts import render,HttpResponse

"""
视图函数
"""
def index(request):
    return HttpResponse("欢迎使用Django")

# 调用函数访问静态页面
def user_list(request):
    return render(request,"user_list.html")

def tpl(request):
    name = "胡图图"                     # str
    roles = ["大胖子","小学生","萝卜头"]  # list
    user_info={
    
    "name":"胡图图","age":3}
    return render(request,'tpl.html',{
    
    "n1":name,"n2":roles,"n3":user_info})  # 转化
  • html file
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--输出tpl函数中变量和列表-->
<div>{
   
   { n1 }}}</div>
<div>{
   
   { n2 }}}</div>
<!--使用索引访问列表-->
<div>{
   
   { n2.0 }}</div>
<div>{
   
   { n2.2 }}</div>
<!--for循环列表-->
<div>
    {% for aaa in  n2 %}
    <span>aaa</span>
    {% endfor %}
</div>
<!--显示字典-->
{
   
   { n3 }}
{
   
   { n3.name }}
{% for foo in n3.foo %}
   <li>{
   
   { foo }}</li>
{% endfor %}
<!--条件判断语句-->
{% if n1 == "大胖子" %}
    <h1></h1>
{% else %}
    <h1></h1>
{% endif %}

</body>
</html>
④Case: Output the data of the python program to the page

7. Request and response

①Request
Ⅰ. Obtain request method

Get the request method of the front end

request.method

Ⅱ. Obtain the value passed by the get request

The return value is a dictionary

# 获取在url上传递的值,get请求    url: http://127.0.0.1:8080/UrlDate/?n=1  传递了n=1
request.GET  # 返回值: <QueryDict: {'n': ['1']}>   返回的是字典

Ⅲ. Obtain post request

request.POST
②Response
  • HttpResponse()
  • render()
Ⅰ. Return string
  # [响应]将字符串返回给请求者
  return HttpResponse("返回内容")
Ⅱ. Return to a page and return template variables
return render(request, 'url_date.html', {
    
    "uuu": "大胖子"})
  • html file
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{
   
   { uuu }}  <!--浏览器效果:大胖子-->
</body>
</html>
Ⅲ. Redirect
  • Jump page
 # 重定项,访问次函数方法就会跳转页面
 return redirect("https://blog.csdn.net/tyx2985491138?type=blog")

8. User login case

  • views.py file
from django.shortcuts import render, HttpResponse, redirect
"""
登录页面
"""
def login_a(request):
    # 用户第一次访问是以get请求,然后通过前端from表单提交是post请求,从第二次就是post请求
    if request.method == "GET":
        return render(request,'login.html')
    else:
        username = request.POST.get("user")
        password = request.POST.get("pwd")

        if username == 'root' and password == '123456':
            # return HttpResponse("登录成功")
              return redirect("https://blog.csdn.net/tyx2985491138?type=blog") # 登录成功重定向跳转页面
        else:
            # return HttpResponse("登录失败")
            return render(request,'login.html',{
    
    "loginaaa":"用户名密码错误"}) # 使用模板语言让登录失败可以返回到前端
  • urls.py file
from django.urls import path
from app01 import views # 导入app01中views.py文件(书写函数的文件夹)
urlpatterns = [
    
# 用户登录案例
    path('login/', views.login_a),
]
  • html file
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
</head>
<body>

<h1>用户登录</h1>
<form method="post" action="/login/">
    {% csrf_token %}  <!--403报错使用-->
    <input type="text" name="user" placeholder="用户名">
    <input type="password" name="pwd" placeholder="密码">
    <input type="submit" value="提交">
    <span style="color: red">{
   
   { loginaaa }}</span>  <!--用于登录失败后从后端返回显示语句->
</form>

</body>
</html>

9. Summary

2. Operation database

  • The implementation technology usesORM operation database

1. Quick start

①py native code to connect to the database
  • pymysqlconnection number installation
import pymysql

# 1.连接MySQL
conn = pymysql.connect(host="127.0.0.1", port=3306, user='root', passwd="root123", charset='utf8', db='unicom')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)

# 2.发送指令
cursor.execute("insert into admin(username,password,mobile) values('wupeiqi','qwe123','15155555555')")
conn.commit()

# 3.关闭
cursor.close()
conn.close()
②ORM connects to the database
  • Django's internal framework is used to simplify pymsql connection to the database
  • can help us:
    • Create, modify, and delete tables in the database [Cannot create database]
    • Operation databaseNo need to write sql statements
Ⅰ. Install third-party software packages
  • Install mysqlclient
pip install mysqlclient
  • Error resolution

  • File address: https://wwt.lanzouy.com/ieVdA0es8rad Password: evzg

  • Mysqlclient installation failure solution](http://t.csdn.cn/UetPe)

Ⅱ. Create database
Ⅰ. Log in to mysql
  • Open a terminal that summarizes your computer
  • Log in to mysql
 mysql -u -root -p    
 或者:mysql -h localhost -u root -p
Ⅱ. Create database
CREATE DATABASE django;
  • Effect
    image-20221029163034942

    Ⅲ. Modify the setting.py configuration file

Line 78

DATABASES = {
    
    
    'default': {
    
    
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'db1',  # 数据库名字
        'USER': 'root',     # 用户名
        'PASSWORD': 'root123',  # 密码
        'HOST': '127.0.0.1',  # 安装了MySQL那台机器的host,本地照写
        'PORT': 3306,     
    }
}
③Operation database

Ⅰ. Create table

  • In the models.py file:
from django.db import models
# django创建数据库表,django会给翻译为sql语句
class UserInfo(models.Model):
    name = models.CharField(max_length=32)
    password = models.CharField(max_length=64)
    age = models.IntegerField()
    
class Role(models.Model):
     caption = models.CharField(max_length=16)
  • Automatically generated sql statements
"""
   生成的sql语句
"""
    # 写了如上的类dajngo会自动生成如下sql语句,表面是app名+类名
create table app01_userinfo(
    id bigint auto_increment primary key,
    name VARCHAR(32),
    password VARCHAR(64)
    age int
)
  • Excuting an order

    generate sql

python manage.py makemigrations
python manage.py migrate  
  • Effect
    image-20221029162554808
Ⅱ. Delete table
  • Delete the Role table

  • After creating the table, just comment or delete the class of the table to be deleted in the models.py file.

from django.db import models
# django创建数据库表,django会给翻译为sql语句
class UserInfo(models.Model):
    name = models.CharField(max_length=32)
    password = models.CharField(max_length=64)
    age = models.IntegerField()
    
# class Role(models.Model):
#     caption = models.CharField(max_length=16)
  • Execute code
python manage.py makemigrations
python manage.py migrate  
Ⅲ. Modification table
  • Just modify the models.py file after creating the table
class UserInfo(models.Model):
    name = models.CharField(max_length=32)
    password = models.CharField(max_length=64)  # varchar字段
    # 新增列需要设置默认值
    age = models.IntegerField(default=2)   # 设置默认值为2
    data = models.IntegerField(null=True,blank=True)  # int类型,字段名data,可以为空
  • and then execute the code
python manage.py makemigrations
python manage.py migrate  

2. Add, delete, modify and check

①Add data
Ⅰ. Add console
  • grammar

    • create() method
    • In the models.py file
    类名或表名.objects.create(字段名='数据')
    
  • List:

Department.objects.create(title='销售部')
UserInfo.objects.create(name="大胖子",password="110110110",age=3,data=172)
  • Execution code:
python manage.py makemigrations
python manage.py migrate  
Ⅱ. Integrate front-end to add data
  • views.py file
from app01 import models
def orm(request):
    # 在Department文件中执行
    models.Department.objects.create(title="大胖子")
    models.Department.objects.create(title="胡图图")
    return HttpResponse("成功")
  • urls.py file
# 测试orm添加数据,访问orm后端执行sql
    path('orm/',views.orm())
②Delete data
Ⅰ. Console deletion
  • Syntaxdelete()
# 删除数据
UserInfo.objects.all(id=3).delete()   # 删除UserInof表中id为3的数据
Department.objects.all().delete()   # 删除全部
③Get data
  • grammar

    类名或表名.objects.all()   # 返回值是列表里面封装了对象,每行的对象。每一行数据都是一个对象
    
    • all taken all()
# 获得数据
data_list = UserInfo.objects.all()   # 返回值是个列表,封装了每一行的对象
for obj in data_list:   # 使用for循环遍历,每行的对象并取出每行表的数据
    print(obj.id,obj.name,obj.password,obj.age,obj.data)
  • Specify condition to obtainfilter()
    • first(): Get the first object in the list
obj = UserInfo.objects.filter(id=1).first()  # 直接返回一个对象
print(obj.id,obj.name,obj.password,obj.age,obj.data)
④Modify data
  • update()
UserInfo.objects.filter(name="大胖子").update(age=999)

3. Comprehensive case: user management

  • create app
python manage.py startapp app1

3. Django development

  • Employee management system, basics
  • Demo2

1. Project initialization

①. Delete redundant code

image-20221030111336543

②、Create app

In the pycharm project terminal

python manage.py startapp app1
③、Register app

Fill in the path

'app01.apps.App01Config'

image-20221030110746270

2. Write a database

①. Design table structure

image-20221030120815523

②.Create table
from django.db import models

"""
部门表
"""
class Department(models.Model):
    # 设置自增的,注释掉是因为pycharm创建项目会默认自动创建id
    # models.BigAutoField(verbose_name='标题',primar=True)       
    title = models.CharField(verbose_name='标题', max_length=32)  # verbose_name='标题',著名此表头的作用
 

"""
员工表
 """
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="年龄")  # 整型

    #  max_digits:数字长度10,decimal_places:小数有两位,default默认初始值为0
    account = models.DecimalField(verbose_name='账户余额', max_digits=10, decimal_places=2, default=0)
    create_time = models.DateTimeField(verbose_name='入职时间')
    
    """外键部门表"""
    # 无约束
    # depart_id = models.BigIntegerField(verbose_name='部门表ID')   

    # 有约束
    # - to :表与哪张表关联   ,  to_fields :表与哪个字段关联
    # on_delete=models.CASCADE():表示级联删除,部门表的IT部门被删除了,所属IT部门的员工表的员工会连带删除
    # 表头名:depart_id  django会根据to_fields自动生成列名
    depart = models.ForeignKey(to=Department, to_field="id",on_delete=models.CASCADE  
    
    # 置空
    # blank=True:可以为空值,
    # on_delete=models.SET_NULL():部门表的IT部门被删除了,所属IT部门的员工的部门字段会变为null空值,即无所属部门
    # depart = models.ForeignKey(to=Department, to_fields="id", blank=True,on_delete=models.SET_NULL())
    
    """
     在django中约束。在数据库存储的是1和2,约束只能选择哪一部分
    """
    gender_choices= (
        (1, '男'),
        (2, '女')
    )
    gender = models.SmallIntegerField(verbose_name='性别',choices=gender_choices)
③.Create database
  • Open command terminal
win+r  ,输入cmd
  • Log in to mysql
mysql -h localhost -u root -p
  • Create the database djangoDemo2 and set the encoding to UTF-8
 create database djangoDemo2 DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
  • View database
show databases;
④. Modify the Settings.py configuration file
  • Comment out all the original DATABSES, line 77 and replace it directly as follows:Pay attention to modifications Database name, username, password
DATABASES = {
    
    
    'default': {
    
    
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'djangoDemo2',  # 数据库名字
        'USER': 'root',
        'PASSWORD': '1234',
        'HOST': '127.0.0.1',  # 哪台机器安装了MySQL
        'PORT': 3306, 
    }
}
⑤. Django command generation table
python manage.py makemigrations
python manage.py migrate
⑥、Add data

Console added

  • Select database
USE djangodemo2
  • adding data
insert into app_01 app1_department(title) values("销售部"),("IT部");

3. Import static files

  • Unzip it and put it in the app01 directory

    • static file: https://wwt.lanzouy.com/iFXts0eu1vre

    • templates:https://wwt.lanzouy.com/ikXAv0eu1vtg

4. Department management function

Pay attention to the guide package. The function added later in this project will not write the guide package. It will only write the package that has not been guided by the front end, so as to proceed step by step.

①、Inquiry
  • views.py file
from django.shortcuts import render
from app1 import models

def depart_list(request):
    """部门列表"""
    # 从数据库中获取全部部门信息
    query_set = models.Department.objects.all()
    return render(request, 'depart_list.html', {
    
    'queryset': query_set})  # 把数据库信息返回给前端遍历显示
  • urls.py
from django.urls import path
from app1 import views

urlpatterns = [
    # path('admin/', admin.site.urls),
    path('depart/list/', views.depart_list)]
  • depart_list.html file
    image-20221031105103594
②、Add new
  • views.py
from django.shortcuts import redirect

def depart_add(request):
    if request.method == 'GET':
        return render(request, 'depart_add.html')
    # 获取用户提交过来的数据
    title = request.POST.get("title")
    # 保存到数据库
    # create(数据库的列名 = 前端提交的数据)
    models.Department.objects.create(title=title)
    # 重定向,显示所有部门
    return redirect('/depart/list/')
  • urls.py
  # 新增页面
    path('depart/add/', views.depart_add),
  • depart_add.html

No modification, just use it

③、Delete
  • vuews.py
def depart_delete(request):
    # 获取ID
    # http://127.0.0.1:8000/depart/delete/?nid=1
    nid = request.GET.get('nid')
    # 删除
    models.Department.objects.filter(id=nid).delete()
    # 重定向
    return redirect('/depart/list/')    # 重定向路径需要在前面也加个 / 千万要注意
  • urls.py
# 删除部门
    path('depart/delete/', views.depart_delete),
  • depart_list.html
    image-20221031161850536
④、Edit
  • views.py
def depart_edit(request, nid):
    if request.method == 'GET':
        row_update = models.Department.objects.filter(id=nid).first()
        print(f"被修改的部门id和名称:{
      
      row_update.id, row_update.title}")
        return render(request, 'depart_edit.html', {
    
    'row_update': row_update.title})
    # 获取用户提交的标题
    title = request.POST.get("title")
    # 根据id找到数据库中的数据并进行更新
    models.Department.objects.filter(id=nid).update(title=title)
    # 重定向返回主页面
    return redirect("/depart/list/")
  • urls.py
# 编辑部门
    # http://127.0.0.1:8000/depart/4/edit  中间的数字用来分辨是哪个部门
    path('depart/<int:nid>/edit/', views.depart_edit),
  • depart.html
    image-20221031165430464

  • depart_list.html
    image-20221031165509981

5. Django’s html inheritance

  • Terms and ConditionsLanguage
{% extends 'layout.html' %}   <!--继承 页面-->
{% block content %}
    这里写html页面样式,子类自己独有的
{% endblock %}
  • effect

You can display all pages of the parent class, andmake modifications on their basis to realize your own unique pages, Avoid duplication of code

  • parentclass.html
<h1>这个是父类的,子类继承后也会有</h1>
<div>
    {% block content %}{% endblock %}  <!--父类给子类自己编写的地方,标签对应着-->
</div> 
  • subclass.html
{% extends '父类.html' %} 
{% block content %}
    
{% endblock %}

4. Management function of employee table

①. Execute sql to let the database have data

Log in to the database: win+r, cmd. mysql -u root -p

Select database: USE dangodemo2

  • Execute sql to add data
insert into app1_userinfo(name,password,age,account,create_time.grdner,depart_id) values("胡图图","110110110",7,100.64,"2022-11-1",2,1);
insert into app1_userinfo(name,password,age,account,create_time.grdner,depart_id) values("大胖子","110110110",7,100.64,"2022-11-1",2,1);
②、Inquiry
  • views.py
def user_list(request):
    # 获取用户全部值
    user_info_list = models.UserInfo.objects.all()

    # """用python语法,这里是后端的获取数据,加深理解代码 ,把对象传入前端在前端获取值  """
    # for obj in user_info_list:
    #     print(obj.id, obj.name, obj.account, obj.create_time.strftime("%Y-%m-%d"), obj.get_gender_display(),
    #           obj.depart_id, obj.depart.title)
    #     """重点"""
    #     obj.create_time.strftime("%Y-%m-%d") # strftime("%Y-%m-%d"):表显示年月日
    #     obj.get_gender_display()  # 创建员工表时给其定义了,存储数据库1代表是男,2代表是女。这个方法意思是获取规则
    #     obj.depart.title  # 根据id获取,链表的title字段的数据,depart:是models.py文件创建员工表的字段返回值,title则是外键部门表的部门名称字段

    return render(request, 'user_list.html', {
    
    'user_info_list': user_info_list})
  • urls.py
  # 员工管理
    path('user/list/', views.user_list),
  • user_list.html
    image-20221101112103417

  • obj.get_gender_display()

    • The employee table is defined when creating it. Storage database 1 represents male, and 2 represents female. This method means to obtain the rules so that they can be better displayed in the database.

    • Employee table:image-20221101112440388

    • Because the data stored in the database is 1 and 2

  • **obj.depart.title **

    • Because the foreign key is used to obtain the value of another table, title is the department name of the department table, query throughforeign key

    • Employee table:image-20221101112946227

  • Effect:
    image-20221101113400113

  • What the employee table looks like in the database
    image-20221101113441314

③、Add (original method)
  • Original method: trouble
- 用户提交数据没有校验
- 错误,页面上不能显示错误提示	
- 页面上每一个字段都需要我们写一遍
- 关联的数据,需要我们手动循环一个一个获取
  • views.py
def user_add(request):
    # 访问页面是get请求,提交数据是post请求
    if request.method == "GET":
        context = {
    
      
            'gender_choices': models.UserInfo.gender_choices,   # 获取所有性别对应的中文
            'depart_list': models.Department.objects.all()      # 获取所有部门
        }
        return render(request, 'user_add.html', context)  # 将字典返回到前端
    # post请求,通过前端表单name值获取每个表单数据
    user = request.POST.get("ruser")
    pwd = request.POST.get("pwd")
    age = request.POST.get("age")
    ac = request.POST.get("ac")
    ctime = request.POST.get("ctime")
    gender = request.POST.get("gd")
    dp = request.POST.get("dp")
    # 将数据添加到数据库中
    models.UserInfo.objects.create(name=user, password=pwd, age=age, account=ac, create_time=ctime, gender=gender,depart_id=dp)
    # 添加成功返回用户列表页面
    return redirect("/user/list")
  • urls.py
# 添加
    path('user/add/', views.user_add),
  • user_add.html
{% 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="姓名" 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 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 %}
                        </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-primary">提 交</button>
                </form>
            </div>
        </div>
    </div>
{% endblock %}
  • Effect:
    image-20221101170023952
④、Components
  • Form component (small and simple)
  • ModelForm component (easiest)
Ⅰ. From component

Simplify repetitive writing forms

  • 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})
  • user_add.html
<form method="post">
     <!--通过form自动生成表单-->
    {
   
   { form.user }}
    {
   
   { form.pwd }}
    {
   
   { form.email }}
    <!-- <input type="text"  placeholder="姓名" name="user" /> -->
</form>

Use cycle processing

<form method="post">
    {% for field in form%}
    	{
   
   { field }}
    {% endfor %}
    <!-- <input type="text"  placeholder="姓名" name="user" /> -->
</form>
Ⅱ. ModeForm component (recommended)

Further simplify based on the Form component

  • views.py
class MyForm(ModelForm):   # 只有这里有区别
    xxx = form.CharField*("...")    # 自定义字段,也支持自定义生成一个字段
    class Meta:
        model = UserInfo   # UserInfo是创建员工表的类名
        fields = ["name","password","age","account","create_time","depart","gender","xxx"]   # 列表内写的是字段返回值,字段名


def user_add(request):
    if request.method == "GET":
        form = MyForm()
        return render(request, 'user_add.html',{
    
    "form":form})
  • 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)
  • user_add.html
<form method="post">
    {% for field in form%}
    	{
   
   { field }}
    {% endfor %}
    <!-- <input type="text"  placeholder="姓名" name="user" /> -->
</form>
⑤. Implementation and addition of ModelForm component
  • views.py
"""
组件实现添加用户
"""

from django import forms

  # 使用组件
class UserModelForm(forms.ModelForm):
    name = forms.CharField(min_length=3, label="用户名")  # 设置name表单接收数据最小值为3

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

        # 定义表单的class属性
        # widgets = {
    
    
        #     "name": forms.TextInput(attrs={"class": "form-control"}),
        #     "password": forms.TextInput(attrs={"class": "form-control"}),
        # }
        # 简化上面注释的代码
        # 批量生成,让字段加样式
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            # 循环获取所有插件
            for name, field in self.fields.items():  # 这里的name是字段名
                # if name == "password":   # 让password这个字段不加class
                #     continue
                field.widgets.attrs = {
    
    "class": "form-control", "placeholder": field.labels}


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

    # 用户使用post提交数据,数据校验
    form = UserModelForm(data=request.POST)

    if form.is_valid():
        # 逐一校验,数据合法存储入数据库
        # print(form.changed_data)
        form.save()
        return redirect('/user/list/')
    else:
        print(form.errors)  # 将错误信息打印出来
        return render(request, 'user_model_form_add.html', {
    
    'form': form})
  • urls.py
 # 组件实现添加
    path('user/model/form/add', views.user_model_form_add)
  • setting.py sets Chinese

    • LANGUAGE_CODE = 'zh-hans' 
      

    image-20221102125023026

  • user_model_form_add.html

{% 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" novalidate>
                    {% csrf_token %}
                    <!--通过label方法取字段中文名,在models.py中设置字段的时候,verbose_name='姓名'设置的值-->
                    <!-- {
    
    { form.name.label }}: {
    
    { form.name }}-->
                    <!--  {
    
    { form.password.label }}: {
    
    { form.password }}-->
                    
                    <!--使用循环简化如上代码-->
                    {% for field in form %}
                        <div class="form-group">
                            <label>{
   
   { field.label }}</label>
                            {
   
   { field }}
                            <!--如果有错误信息在页面中显示出来,默认是显示的英文,但上一步在Setting设置成中文了-->
                            <span style="color: red;">{
   
   { field.errors.0 }}</span> 
                        </div>
                    {% endfor %}

                    <button type="submit" class="btn btn-primary">提 交</button>
                </form>
            </div>
        </div>
    </div>
{% endblock %}
  • Effect:
    image-20221102130039718

  • models.py

from django.db import models


# Create your models here.
class Department(models.Model):
    """部门表"""
    # models.BigAutoField(verbose_name='标题',primar=True)       # 设置自增的,注释掉是因为pycharm创建项目会默认自动创建
    title = models.CharField(verbose_name='标题', max_length=32)  # verbose_name='标题',著名此表头的作用

    # 类似于to.string方法
    def __str__(self):
        return self.title


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(verbose_name='部门', to=Department, to_field="id", on_delete=models.CASCADE)
   
    gender_choices = (
        (1, '男'),
        (2, '女')
    )
    gender = models.SmallIntegerField(verbose_name='性别', choices=gender_choices)

⑥Edit

Use components

Ⅰ. Ideas
  • Click edit user, jump to the page and carry the id get of the row that needs to be edited.
  • Display some default information (obtained from the database) according to the ID brought over get
  • Submit: post
    • Error prompts, data verification
    • Database update data
Ⅱ. Implementation
  • views.py
def user_edit(request, nid):
    row_object = models.UserInfo.objects.filter(id=nid).first()  # 用于告诉ModelForm是要修改哪一行数据,而不是添加
    # 根据id获取要编辑的那一行数据,从而将原来的值显示在页面上
    if request.method == 'GET':
        # row_object = models.UserInfo.objects.filter(id=nid).first()

        form = UserModelForm(instance=row_object)
        name = row_object.name
        return render(request, 'user_edit.html', {
    
    'form': form, 'name': name})  # 因为form是字典所以可以直接返回,不用大括号
    # post提交表单请求,并数据校验
    # row_object = models.UserInfo.objects.filter(id=nid).first()  # 用于告诉ModelForm是要修改哪一行数据,而不是添加
    form = UserModelForm(data=request.POST, instance=row_object)
    if form.is_valid():
        form.save()  # 数据合法存储入数据库
        return redirect('/user/list/')
    else:
        return render(request, 'user_edit.html', {
    
    'form': form})
  • urls.py
  # 编辑
    path('user/<int:nid>/edit/', views.user_edit),
  • user_edit.html

Need to create it yourself

{% extends 'layout.html' %}

{% block content %}
    <div class="container">
   <div class="panel panel-default">
            <div class="panel-heading">
                <h3 class="panel-title" > 编辑<font color="#1e90ff">{
   
   { name }}</font>用户 </h3>
            </div>
            <div class="panel-body">
                <form method="post" novalidate>
                    {% csrf_token %}
                    <!--通过label方法取字段中文名,在models.py中设置字段的时候,verbose_name='姓名'设置的值-->
                    <!-- {
    
    { form.name.label }}: {
    
    { form.name }}-->
                    <!--  {
    
    { form.password.label }}: {
    
    { form.password }}-->
                    {% for field in form %}
                        <div class="form-group">
                            <label>{
   
   { field.label }}</label>
                            {
   
   { field }}

                            <!--如果有错误信息在页面中显示出来,默认是显示的英文,但上一步在Setting设置成中文了-->
                            <span style="color: red;">{
   
   { field.errors.0 }}</span>
                        </div>
                    {% endfor %}
                    <button type="submit" class="btn btn-primary">提 交</button>
                </form>
            </div>
        </div>

    </div>
{% endblock %}
  • user_list.html

    Modify some content
    image-20221103001721199

  • Effect
    image-20221103001909304

⑦Delete
  • views.py
"""
删除用户
"""


def user_delete(request):
    # 获取ID
    # http://127.0.0.1:8000/depart/delete/?nid=1
    nid = request.GET.get('nid')  # 获取删除按钮通过表单传达过来的nid值
    print(nid)
    models.UserInfo.objects.filter(id=nid).delete()
    return redirect('/user/list/')
  • urls.py
# 删除
    path('user/delete/', views.user_delete),
  • user_list.html
    image-20221103003504460

5. Management of beautiful accounts

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

    # 级别
    level_choice = (
        (1, '1级'),
        (2, '2级'),
        (3, '3级'),
        (4, '4级'),
    )
    level = models.SmallIntegerField(verbose_name="级别", choices=level_choice, default=1)  # 小整型

    status_choices = (
        (1, '已占用'),
        (2, '未占用'),
    )
    status = models.SmallIntegerField(verbose_name="级别", choices=status_choices, default=2)

②List of pretty accounts
  • Display the ID, number, price, level (Chinese), status (Chinese), and operation (edit and delete)

  • views.py

def admin_list(request):
    # order_by()设置排序,select * from 表名 order by level desc
    queryset = models.PrettyNum.objects.all().order_by("-level")  # 加-则表示desc,不加-则表示level
    return render(requesret, 'admin_list.html', {
    
    'queryset': queryset})
  • urls.py
 # 靓号列表主页面
    path('admin/list/', views.admin_list),
  • admin_list.html
{% extends 'layout.html ' %}

{% block content %}
    <div class="container">
        <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>
        <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>
                    <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 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>
{% endblock %}
④Add
  • Validate data using regular expressions

  • views.py

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


class UserModForm2(forms.ModelForm):
    # 验证:方法1
    # mobile = forms.CharField(
    #     label="手机号",
    #     validators=[RegexValidator(r'^\d{11}$', '手机号必须为11位数')]  # 使用正则表达式对其加强校验
    # )

    class Meta:
        model = models.PrettyNum  # 选择表
        fields = ["mobile", "price", "level", "status"]  # 这个是自己选哪些字段
        # fields = "__all__"   # 这个是所有字段
        # exclude = ['level']  # 这个是排除哪个字段

    """加载样式"""

    # def __init__(self, *args, **kwargs):
    #     super().__init__(*args, **kwargs)
    #     # 循环获取所有插件
    #     for name, field in self.fields.items():  # 这里的name是字段名
    #         # if name == "password":   # 让password这个字段不加class
    #         #     continue
    #         # 设置生成的表单class值为"form-control"
    #         field.widgets.attrs = {"class": "form-control", "placeholder": field.labels}

    # 验证:方法2
    def clean_mobile(self):
        txt_mobile = self.cleaned_data["mobile"]  # 获取"mobile"字段用户输入的值
        if len(txt_mobile) != 11:
            # 验证不通过
            raise ValidationError("格式错误")
        # 验证通过,用户输入的值返回
        return txt_mobile


def admin_add(request):
    if request.method == 'GET':
        form = UserModForm2()
        return render(request, 'admin_add.html', {
    
    'form': form})
    # post请求
    form = UserModForm2(data=request.POST)
    if form.is_valid():  # 一个一个校验数据
        form.save()  # 添加进入数据库
        return redirect('/admin/list/')
    else:
        print(form.errors)  # 打印错误信息
        return render(request, 'admin_add.html', {
    
    'form': form})
  • urls.py
    # 添加
    path('admin/add/', views.admin_add),
  • admin_add.html
{% 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" novalidate>
                    {% csrf_token %}
                    {% for field in form %}
                        <div class="form-group">
                            <label>{
   
   { field.label }}</label>
                            {
   
   { field }}
                            <!--如果有错误信息在页面中显示出来,默认是显示的英文,但上一步在Setting设置成中文了-->
                            <span style="color: red;">{
   
   { field.errors.0 }}</span>
                        </div>
                    {% endfor %}

                    <button type="submit" class="btn btn-primary">提 交</button>
                </form>
            </div>
        </div>
    </div>

 {% endblock %}
⑤Edit
  • The phone number cannot be edited, but it still needs to be displayed on the page.

  • After clicking edit, the ID of the beautiful account will be carried to the backend for modification.

  • views.py

def admin_edit(request, nid):
    row_object = models.PrettyNum.objects.filter(id=nid).first()
    if request.method == "GET":
        form = UserModForm2(instance=row_object)
        return render(request, 'admin_edit.html', {
    
    'form': form})
    form = UserModForm2(data=request.POST, instance=row_object)

    if form.is_valid():
        form.save()
        return redirect('/admin/list')
    else:
        return render(request, 'admin_edit.html', {
    
    'form': form})
  • urls.py
  # 编辑
    path('admin/<int:nid>/edit/', views.admin_edit),
  • admin_edit.html
{% 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" novalidate>
                    {% csrf_token %}

                    {% for field in form %}
                        <div class="form-group">
                            <label>{
   
   { field.label }}</label>
                            {
   
   { field }}

                            <!--如果有错误信息在页面中显示出来,默认是显示的英文,但上一步在Setting设置成中文了-->
                            <span style="color: red;">{
   
   { field.errors.0 }}</span>
                        </div>
                    {% endfor %}
                    <button type="submit" class="btn btn-primary">提 交</button>
                </form>
            </div>
        </div>

    </div>
{% endblock %}
⑥Delete
  • views.py
def admin_delete(request):
    nid = request.GET.get('nid')   # 前端的?nid = {
    
    {obj.id}} ,?是占位符代表给nid赋值
    models.PrettyNum.objects.filter(id=nid).delete()
    return redirect('/admin/list')
  • urls.py
# 删除
    path('admin/delete/', views.admin_delete)
  • admin_list.html
    image-20221104174406074
⑦Search query
  • Mobile phone number search
Ⅰ、views.py
  • Modify the original homepage function file
def admin_list(request):
    """查询所有"""
    # order_by()设置排序,select * from 表名 order by level desc
    # queryset = models.PrettyNum.objects.all().order_by("-price")  # 加-则表示正序,不加-则表示倒叙,按价格从高到低
    # return render(request, 'admin_list.html', {'queryset': queryset})
    """搜索查询"""
    data_dict = {
    
    }
    search_data = request.GET.get('q', "")
    if search_data:
        data_dict["mobile__contains"] = search_data
    queryset = models.PrettyNum.objects.filter(**data_dict).order_by("-price")  # 按条件查询
    return render(request, 'admin_list.html', {
    
    'queryset': queryset, 'search_data': search_data})
Ⅱ、urls.py
  • do not change
 # 靓号列表主页面
    path('admin/list/', views.admin_list),
Ⅲ、admin_list.html
  • Modify the original homepage

  • The search box style is bootstrap: official website https://v3.bootcss.com/ style package (bootstrap-3.4.1): https://wwt.lanzouy.com/iYMpj0f7fgze

  • Import style package (bootstrap-3.4.1)

    <script src="{% static 'plugins/bootstrap-3.4.1/js/bootstrap.js' %}"></script>
    
image-20221104192428406
  • code
<div style="float: right;width: 300px">
    <form method="get">
        <div class="input-group">
            <input type="text" name="q" class="form-control" placeholder="搜索号码"
                   value="{
     
     { search_data }}">
            <!--设置表单名字为q,方便url拼接-->
            <span class="input-group-btn">
                <button class="btn b tn-default" type="submit">Go!</button>
        </span>
    </form>
</div>
⑧Page query
  • Add 300 pieces of data to the database

Written in the admin_list function of views.py, you can add 300 pieces of data to the database by accessing it.

# 添加300条数据
    for i in range(300):
        models.PrettyNum.objects.create(mobile="16656678912", price=10, level=1, status=1)
Ⅰ. SQL syntax:
 queryset = models.PrettyNum.objects.filter(**data_dict).order_by("-price")[初始分页索引:结尾id]
Ⅱ、views.py
def admin_list(request):
    """查询所有"""
    # order_by()设置排序,select * from 表名 order by level desc
    # queryset = models.PrettyNum.objects.all().order_by("-price")  # 加-则表示正序,不加-则表示倒叙,按价格从高到低
    # return render(request, 'admin_list.html', {'queryset': queryset})
     """搜索查询"""
    data_dict = {
    
    }
    search_data = request.GET.get('q', "")
    if search_data:
        data_dict["mobile__contains"] = search_data
        
    """分页"""
    # 获取get请求传递的值,page默认值为1,当前页码
    x = int(request.GET.get('p', 1))   # 搜索页码的值
    if x > 1:
        page = x
    page = int(request.GET.get('page', 1))   # 页码输入的当前页码
    # 当前页码,根据用户想要访问的页面,计算出起止位置
    page_size = 10  # 显示10条数据
    start = (page - 1) * page_size
    end = page * page_size
    # 按条件查询
    queryset = models.PrettyNum.objects.filter(**data_dict).order_by("-price")[start:end]
    # 数据总条数
    total_count = models.PrettyNum.objects.filter(**data_dict).order_by("-price").count()
    
    """页码"""
    # 总页码 = 数据总条数/每页显示数据条数
    total_page_count, div = divmod(total_count, page_size)
    # if div:
    #     total_page_count += 1
    # 计算,显示出当前页的前五页和后五页
    plus = 5  # 显示前后页码条数
    if total_page_count <= 2 * plus + 1:  # 小于11页
        # 数据库数据较少没有超过十一页,把所有页码显示
        start_page = 1
        end_page = total_page_count
    else:
        # 数据库数据大于11页数据
        # 当前页小于5(小极值)
        if page <= plus:  # page是前端用户传入要访问的页数
            start_page = 1
            end_page = 2 * plus + 1
        else:
            # 当前页+显示条数>总页码
            if (page + plus) > total_page_count:
                start_page = total_page_count - 2 * plus
                end_page = total_page_count
            else:
                start_page = page - plus  # 当前页减5
                end_page = page + plus  # 后五页

    # 自动生成页码(前端样式)
    page_str_list = []

    # 上一页
    if page > 1:
        prev = '<li><a href="?page={}">上一页</a></li>'.format(page - 1)  # 当前页减一
    else:
        prev = '<li><a href="?page={}">上一页</a></li>'.format(1)
    page_str_list.append(prev)

    # for i in range(1, total_page_count + 1):  # 初始页面和总页码
    for i in range(start_page, end_page + 1):  # +1 因为数据库搜索出来数据总数会少1
        if i == page:  # 判断是当前页的话给其添加样式
            elc = '<li class="active"><a href="?page={}">{}</a></li>'.format(i, i)
        else:
            elc = '<li><a href="?page={}">{}</a></li>'.format(i, i)
        page_str_list.append(elc)  # 将添加进列表中

    # 下一页
    if page < total_page_count:
        prev = '<li><a href="?page={}">下一页</a></li>'.format(page + 1)  # 当前页加1
    else:
        prev = '<li><a href="?page={}">下一页</a></li>'.format(total_page_count)  # 总页码
    page_str_list.append(prev)
    # 尾页
    one = '<li><a href="?page={}">尾页</a></li>'.format(total_page_count)
    page_str_list.append(one)

    # 使用mark_safe包裹就可以将字符串转化成html,在导包的前提下from django.utils.safestring import mark_safe
    page_string = mark_safe("".join(page_str_list))

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

  • urls.py

No changes

Ⅲ、admin_list.html
<!--分页按钮-->
        <ul class="pagination">
            {#        <li><a href="?page=1">1</a></li>#}
            {#        <li><a href="?page=2">2</a></li>#}
            {#        <li><a href="?page=3">3</a></li>#}
            {#        <li><a href="?page=4">4</a></li>#}
            {#        <li><a href="?page=5">5</a></li>#}
            {
   
   { page_string }}   <!--页码-->
            <!--分页搜索页码-->
            <li>
                <form method="get">
                    <div class="input-group">
                        <input type="text" name="p" class="form-control" placeholder="搜索号码"
                               value="{
     
     { search_data }}">
                        <!--设置表单名字为q,方便url拼接-->
                        <span class="input-group-btn">
                            <button class="btn b tn-default" type="submit">Go!</button>
                    </span>
                    </div>
                </form>
            </li>
        </ul>
Ⅳ. Effect:

image-20221106230715505

⑨. Use encapsulation to simplify paging code (encapsulation paging)
Ⅰ、views.py
def admin_list(request):
    """查询所有"""
    # order_by()设置排序,select * from 表名 order by level desc
    # queryset = models.PrettyNum.objects.all().order_by("-price")  # 加-则表示正序,不加-则表示倒叙,按价格从高到低
    # return render(request, 'admin_list.html', {'queryset': queryset})
    """搜索查询"""
    data_dict = {
    
    }
    search_data = request.GET.get('q', "")
    if search_data:
        data_dict["mobile__contains"] = search_data

    """分页"""

    from app1.utils.pagination import Pagination

    # 按条件查询
    # queryset = models.PrettyNum.objects.filter(**data_dict).order_by("-price")[page_object.start: page_object.end]
    queryset = models.PrettyNum.objects.filter(**data_dict).order_by("-price")

    page_object = Pagination(request, queryset)  # 获取自定义组件类

    page_queryset = page_object.page_queryset  # 获取分页完的数据

    page_string = page_object.html()  # 生成的页码

    context = {
    
    
        "queryset": page_queryset,
        "search_date": search_data,  # 分完页的数据
        "page_string": page_string  # 生成的页码
    }

    return render(request, 'admin_list.html', context)  # 使用组件后
Ⅱ、pagination.py
  • Create paginaion.py in the utils folder in app1

  • Encapsulate page number generation, data acquisition, and front-end page number generation into the Pagination class in the pagination.py file

"""
自定义分页组件
"""
from django.utils.safestring import mark_safe
from django import forms


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:显示当前页的,前、后几页(页码)
        """
        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")  # 把request获取前端get请求的方法,封装到page中,页码
        if page.isdecimal():  # 处理页码,判断页码是否是正常传入数字,而不是字符串
            page = int(page)
        else:
            page = 1  # 前端传入页码不规范,则默认为1
        self.page = page
        self.page_size = page_size
        # 计算分页页码,sql值
        self.start = (page - 1) * page_size
        self.end = page * page_size

        self.page_queryset = queryset[self.start: self.end]  # 分完页的数据

        # total_count = models.PrettyNum.objects.filter(**data_dict).order_by("-price").count()
        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

Ⅲ. Effect

image-20221107161937082

6. Expanded functions (1)

1. Time plug-in

  • Plug-in link: https://wwt.lanzouy.com/imuOd0fe3opc

  • Used to beautify and simplify filling in the entry time, you can directly click xuan’ze

2. Module development

  • Divide user management, department management, and pretty account management in views.py into three py files.The code does not need to be changed, just Change the function path in urls.py

  • views folder
    image-20221109113859782

  • urls.py

The focus is on import packages and function paths

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

from app1.views import admin, list, user   # 导入views文件

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

    # 部门管理

    # 主页面、列表页面
    path('depart/list/',list.depart_list),
    # 新增部门
    path('depart/add/', list.depart_add),
    # 删除部门
    path('depart/delete/', list.depart_delete),
    # 编辑部门
    # http://127.0.0.1:8000/depart/4/edit  中间的数字用来分辨是哪个部门
    path('depart/<int:nid>/edit/', list.depart_edit),

    # 员工管理
    path('user/list/', user.user_list),
    # 添加
    path('user/add/', user.user_add),
    # 组件实现添加
    path('user/model/form/add', user.user_model_form_add),
    # 编辑
    path('user/<int:nid>/edit/', user.user_edit),
    # 删除
    path('user/delete/', user.user_delete),

    # 靓号管理
    # 靓号列表主页面
    path('admin/list/', admin.admin_list),
    # 添加
    path('admin/add/', admin.admin_add),
    # 编辑
    path('admin/<int:nid>/edit/', admin.admin_edit),
    # 删除
    path('admin/delete/', admin.admin_delete)

]

7. Administrator

1. Create a database

  • models.py
class VIP(models.Model):
    username = models.CharField(verbose_name="用户名", max_length=32)
    password = models.CharField(verbose_name="密码", max_length=64)
  • Execute code
python manage.py makemigrations
python manage.py migrate

2. Administrator list

  • VIP.py

Create VIP.py file in views folder

from django.shortcuts import render, redirect
from app1 import models
# 分页自定义模块
from app1.utils.pagination import Pagination

"""
管理员列表
"""
def VIP_list(request):
    # 搜索功能
    # 构造搜索条件
    data_dict = {
    
    }
    search_data = request.GET.get('q', "")
    if search_data:
        data_dict["username__contains"] = search_data
    # 根据条件去数据库获取
    queryset = models.VIP.objects.filter(**data_dict)
    # 分页
    page_object = Pagination(request, queryset)

    queryset = {
    
    
        'queryset': queryset,
        'page_string': page_object.html(),
        'search_data': search_data,
    }
    return render(request, 'VIP_list.html', queryset)

  • urls.py
# 主页面
path('VIP/list', VIP.VIP_list),
  • VIP.html
{% extends 'layout.html ' %}

{% block content %}
    <div class="container">
        <div style="margin-bottom: 10px" class="clearfix">
            <a class="btn btn-success" href="/VIP/add">
                <span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>
                添加管理员
            </a>

            <div style="float: right;width: 300px">
                <form method="get">
                    <div class="input-group">
                        <input type="text" name="q" class="form-control" placeholder="关键字"
                               value="{
     
     { search_data }}">   <!--将搜索的字显示在input框中-->
                        <!--设置表单名字为q,方便url拼接-->
                        <span class="input-group-btn">
                            <button class="btn b tn-default" type="submit">搜索查询</button>
                    </span>
                    </div>
                </form>
            </div>
        </div>
        <div class="panel panel-default">
            <!-- Default panel contents -->
            <div class="panel-heading">
                <span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>
                管理员账户列表
            </div><!-- /input-group -->

            <!-- Table -->
            <table class="table table-bordered">
                <thead>
                <tr>
                    <th>ID</th>
                    <th>号码</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 href="/VIP/{
     
     {obj.id}}/reset/">重置密码</a>
                        </td>
                        <td>
                            <a class="btn btn-primary btn-xs" href="/VIP/{
     
     { obj.id }}/edit">编辑</a>
                            <a class="btn btn-danger btn-xs" href="/VIP/delete/?nid={
     
     { obj.id }}">删除</a>
                        </td>
                    </tr>
                {% endfor %}

                </tbody>
            </table>
        </div>
        <!--分页按钮-->
        <ul class="pagination">
            {
   
   { page_string }}   <!--页码、分页搜索-->
        </ul>
    </div>
{% endblock %}

3. Add

  • Focus: Password verification

  • VIP.py

under views folder

from django import forms
from django.core.exceptions import ValidationError
from app1.utils.bootstrap import BootStrapModeForm  # 创建input框
from app1.utils.encrypt import md5  # 加密


# 使用组件创建input框
class VIPModelForm(BootStrapModeForm):  # 或者继承forms.Modelform
    # 额外生成一个确认密码表单
    confirm_password = forms.CharField(
        label="确认密码",
        widget=forms.PasswordInput(render_value=True)  # 输入密码时候不会显示密码,render_value=True表示密码错了密码不需要重新输入
    )

    class Meta:
        model = models.VIP
        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)  # 调用加密函数给input框的数据加密,并将加完密的函数返回,则下面方法获取该表单都是加密了的

    # 钩子函数,获取指定字段用户输入的值
    def clean_confirm_password(self):
        #   form.cleaned_data  获取校验成功后所有数据,具体看上面定义的clean_confirm_password钩子函数
        password = self.cleaned_data.get("password")  # 因为上面给表单加密了,所以这里获取的值是加密的值
        confirm = md5(self.cleaned_data.get("confirm_password"))  # 获取确认用户表单,用户输入的值,并使其加密从而可以使密文和密文进行比较
        if confirm != password:
            raise ValidationError("密码不一致")  # 抛出异常到前端,需要导包
        return confirm  # 这里返回的值就是存储入数据库的值


def VIP_add(request):
    title = "添加管理员"
    if request.method == "GET":
        form = VIPModelForm()
        return render(request, 'change.html', {
    
    "title": title, "form": form})
    # post请求校验数据,并跳转主页面
    form = VIPModelForm(data=request.POST)
    if form.is_valid():
        # 上面的钩子函数也是校验数据
        form.save()
        return redirect('/VIP/list')
    # 有错误信息,则还是在添加页面,并可以看到错误信息
    return render(request, 'change.html', {
    
    "title": title, "form": form})
  • urls.py
    # 添加管理员
    path('VIP/add', VIP.VIP_add),
  • change.html
    • Because the added page files are almost the same, you can use one file and just customize it where you need it to be different.
<!--添加用户父类模板-->
{% extends 'layout.html' %}
 {% block content %}
       <div class="container">
        <div class="panel panel-default">
            <div class="panel-heading">
                <h3 class="panel-title"> {
   
   { title }}</h3>   <!--传什么值这里显示什么值,添加用户页面就显示添加用户-->
            </div>
            <div class="panel-body">
                <form method="post" novalidate>
                    {% csrf_token %}
                    {% for field in form %}
                        <div class="form-group">
                            <label>{
   
   { field.label }}</label>
                            {
   
   { field }}
                            <!--如果有错误信息在页面中显示出来,默认是显示的英文,但上一步在Setting设置成中文了-->
                            <span style="color: red;">{
   
   { field.errors.0 }}</span>
                        </div>
                    {% endfor %}

                    <button type="submit" class="btn btn-primary">提 交</button>
                </form>
            </div>
        </div>
    </div>

 {% endblock %}
  • encrypt.py
    • Customized password verification tool class, placed under app01/utils/
    • The password and confirmation password comparison also uses the encrypted password for comparison.
    • The principle is to embed the password into a randomly generated string in the Django configuration file. See line 8 of the code for details.
import hashlib
from django.conf import settings

"""
给字符串加密
"""
def md5(data_string):
    obj = hashlib.md5(settings.SECRET_KEY.encode('utf-8'))   # SECRET_KEY是django的配置文件Setting随机生成的一个字符串
    obj.update(data_string.encode('utf-8'))
    return obj.hexdigest()

4. Edit

  • VIP.py

views folder subfiles

class VIPEditModelForm(BootStrapModeForm):
    class Meta:
        model = models.VIP
        fields = ["username"]  # 只允许修改用户名


def VIP_edit(request, nid):
    title = "编辑管理员"
    row_object = models.VIP.objects.filter(id=nid).first()

    if not row_object:  # 判断是否有这个用户,没有就证明id为错误的
        return redirect('/VIP/list')
    if request.method == "GET":
        form = VIPEditModelForm(instance=row_object)  # 将原来的值显示在页面上
        return render(request, 'change.html', {
    
    "title": title, "form": form})
    # 提交数据
    form = VIPEditModelForm(data=request.POST, instance=row_object)
    if form.is_valid():
        form.save()
        return redirect('/VIP/list')
    return render(request, 'change.html', {
    
    "title": title, "form": form})
  • urls.py
  # 编辑
    path('VIP/<int:nid>/edit/', VIP.VIP_edit),
  • change.html
    • Use the same html as adding an administrator as above, but there are some differences in the views.py file (VIP.py)

5. Delete

  • VIP.py
def VIP_delete(request):
    nid = request.GET.get('nid')
    models.VIP.objects.filter(id=nid).delete()
    return redirect('/VIP/list')
  • urls.py
   # 删除
    path('VIP/delete/', VIP.VIP_delete),
  • Pay attention to changing the path of the button on the administrator's home page.

6. Reset password

  • views.py
# 使用组件创建input框
class VIPresetModelForm(BootStrapModeForm):  # 或者继承forms.Modelform

    # 额外生成一个确认密码表单
    confirm_password = forms.CharField(
        label="确认密码",
        widget=forms.PasswordInput(render_value=True)  # 输入密码时候不会显示密码,render_value=True表示密码错了密码不需要重新输入
    )

    class Meta:
        model = models.VIP
        fields = ["password", "confirm_password"]
        # 给原有字段,生成的表单添加额外修饰
        widgets = {
    
    
            "password": forms.PasswordInput(render_value=True)
        }

    # 加密
    def clean_password(self):
        pwd = self.cleaned_data.get("password")
        md5_pwd = md5(pwd)  # 调用加密函数给input框的数据加密,并将加完密的函数返回,则下面方法获取该表单都是加密了的

        # 去数据库校验之前密码和新输入的密码是否一致
        exists = models.VIP.objects.filter(id=self.instance.pk, password=md5_pwd)  # 返回的是布尔值
        if exists:
            raise ValidationError("密码使用过,请尝试重新输入密码")

        return md5_pwd

    # 钩子函数,获取指定字段用户输入的值
    def clean_confirm_password(self):
        #   form.cleaned_data  获取校验成功后所有数据,具体看上面定义的clean_confirm_password钩子函数
        password = self.cleaned_data.get("password")  # 因为上面给表单加密了,所以这里获取的值是加密的值
        confirm = md5(self.cleaned_data.get("confirm_password"))  # 获取确认用户表单,用户输入的值,并使其加密从而可以使密文和密文进行比较
        if confirm != password:
            raise ValidationError("密码不一致")  # 抛出异常到前端,需要导包
        return confirm  # 这里返回的值就是存储入数据库的值


def VIP_reset(request, nid):
    row_object = models.VIP.objects.filter(id=nid).first()

    if not row_object:
        return redirect('/VIP/list')

    title = "重置用户:{} 密码".format(row_object.username)

    if request.method == 'GET':
        form = VIPresetModelForm()
        return render(request, 'change.html', {
    
    "title": title, "form": form})

    # 提交数据
    form = VIPresetModelForm(data=request.POST, instance=row_object)
    if form.is_valid():
        form.save()
        return redirect('/VIP/list')
    return render(request, 'change.html', {
    
    "title": title, "form": form})
  • urls.py
 # 重置密码
    path('VIP/<int:nid>/reset/', VIP.VIP_reset),
  • BootStrap.py

Customized toolkit, because the password reset uses form instead of ModelForm, so use inheritance to modify it.

from django import forms

"""
添加样式,实际是给每个表单创建class属性
"""


class BootStrap:
    def __int__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 循环Modelform中所有字段,给每个字段的插件设置
        for name, field in self.field.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
                }


class BootStrapModeForm(BootStrap, forms.ModelForm):
    pass


class BootStrapForm(BootStrap, forms.Form):
    pass
  • change.html

8. Expanded functions (2)

1. Administrator login

  • views.py
from django.shortcuts import render, redirect, HttpResponse
from app1.utils.encrypt import md5
from app1 import models

"""
登录
"""
# 使用form做
from django import forms
from app1.utils.bootstrap import BootStrapForm


class LoginForm(BootStrapForm):  # Form需要自定义字段,
    username = forms.CharField(
        label="用户名",
        widget=forms.PasswordInput(attrs={
    
    "class": "form-control"})  # 定义标签
    )
    password = forms.CharField(
        label="密码",
        widget=forms.PasswordInput(attrs={
    
    "class": "form-control"}, render_value=True)  # 密码标签

    )

    # 钩子函数,给密码加密,从而可以使数据与后端密码比较
    def clean_password(self):
        pwd = self.cleaned_data.get("password")
        return md5(pwd)


# # ModelForm则是从数据库中拿字段,去数据库拿安全性差些
# class LoginModelForm(forms.ModelForm):
#     class Meta:
#         model = models.VIP
#         fields = ['username', 'password']

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():
        # form.cleaned_data获取前端用户输入的值(用户名和密码)
        # print(form.cleaned_data)  # 验证成功,获取到的用户名和密码,因为form与modelForm不同,form不与数据库关联不可以直接写入数据库
        # 去数据库校验用户名和密码是否正确
        # 因为上面有个钩子函数,给form.cleaned_data方法加密了,所以这个获取的也是加密的密码
        first = models.VIP.objects.filter(**form.cleaned_data).first()
        # first = models.VIP.objects.filter(username="xxx", password="xxx").first()  ,如上代替具体见搜索手机号详解,只有字典可以这么用
        if not first:
            form.add_error("password", "用户密码错误")  # 给password字段主动添加错误,password从上面18行创建字段表单获取的
            # 存储session到数据库django_session表中自动创建的,也就是用户cookie的凭证,使用cookie的时候得用session来辨别用户
        # request.session["info"] = {'id': filter.id, 'username': first.username}  # 获取用户id和username存储到session中,来当用户凭证
        request.session["info"] = first.username   # 这里只存储个username
        return redirect("/VIP/list/")
    return render(request, 'login.html', {
    
    'form': form})
  • urls.py
 # 登录
    path('login/', login.login),
  • 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>
  • cookies and sessions
request.session["info"] = {
    
    'id': filter.id, 'username': first.username}  # 获取用户id和username存储到session中,来当用户凭证
        request.session["info"] = first.username   # 这里只存储个username

2. Verify user login

①Verify login

The principle is to obtain the cookie through the session. If there is no cookie, there will be no login.

 # 校验是否登录,没登陆跳转登录页面
    info = request.session.get("info")   # 获取session的info字典的用户cookie凭证
    if not info:
        return redirect('/login/')
②Middleware verification

Because of the use of intermediate keys, there is no need to use the above code in each views.py to verify login. You only need to define a middleware, and the code in the function file does not need to be changed.

  • Create middleware

Create a folder middleware under app1, and then create auth.py. The code inside is as follows

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

# 中间件校验用户是否登录
class AuthMiddleware(MiddlewareMixin):
    """中间件1"""

    def process_request(self, request):
        # 排除那些不需要登录校验的页面
        if request.path_info == "/login/":
            return
        # 读取当前访问的用户session信息,如若读到证明登录过,则继续执行
        info_dict = request.session.get("info")
        if info_dict:
            return
        # 没有登录信息
        return redirect('/login/')
  • Register middleware

In the setting.py file, each class is a middleware

image-20221112112435796

3. Log out

  • principle

Just delete the cookies, because there are no cookies and you need to log in again

  • login.py
def logout(request):
    request.session.clear()  # 将当前用户的session删除掉,等于将用户cookie删除
    return redirect('/login/')
  • urls.py
 # 注销
    path('logout/', login.logout),
  • layout.html

Change the execution function address of the html logout tag

<li><a href="/logout/">注销</a></li>

4. Display the current user name

As the user changes the username, it will also change automatically.

  • Get the current user’s account and password
# 通过session来获取当前用户的账号和密码
    info = request.session.get("info")   # 获取session的info字典的用户cookie凭证
    # 取值,存储在session中用户输入的账号和密码
    info["id"]   
    info["username"]
  • Application in front-end
{
   
   { request.session.info.username }}
  • Effect:

image-20221112115837199

  • What is obtained is the value stored in the session in the login.py folder:

image-20221112115758287

5. Picture verification code

①pillow plug-in

Automatically generate verification code image plug-in

Ⅰ. Install plug-in
pip install pillow

Or install it in the project-python interpreter in pycham settings

Ⅱ.Use

References: https://www.cnblogs.com/wupeiqi/articles/5812291.html

Ⅲ. Font file

Font for generating verification code:

Ⅳ. Generation and verification of verification code
  • code.py

Customized toolkit file, pay attention to modifying the 4th linefont path. This font is in the same directory as app1< /span>

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

def check_code(width=120, height=30, char_length=5, font_file='Monaco.ttf', font_size=28):
    code = []
    img = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255))
    draw = ImageDraw.Draw(img, mode='RGB')

    def rndChar():
        """
        生成随机字母
        :return:
        """
        # return str(random.randint(0, 9))  # 生成数字
        return chr(random.randint(65, 90))  # 生成字母

    def rndColor():
        """
        生成随机颜色
        :return:
        """
        return random.randint(0, 255), random.randint(10, 255), random.randint(64, 255)

    # 写文字
    font = ImageFont.truetype(font_file, font_size)
    for i in range(char_length):
        char = rndChar()
        code.append(char)
        h = random.randint(0, 4)
        draw.text([i * width / char_length, h], char, font=font, fill=rndColor())

    # 写干扰点
    for i in range(40):
        draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())

    # 写干扰圆圈
    for i in range(40):
        draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())
        x = random.randint(0, width)
        y = random.randint(0, height)
        draw.arc((x, y, x + 4, y + 4), 0, 90, fill=rndColor())

    # 画干扰线
    for i in range(5):
        x1 = random.randint(0, width)
        y1 = random.randint(0, height)
        x2 = random.randint(0, width)
        y2 = random.randint(0, height)

        draw.line((x1, y1, x2, y2), fill=rndColor())

    img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
    return img, ''.join(code)
  • login.html
    <div class="form-group">
            <label for="id_code">图片验证码</label>
            <div class="row">
                <div class="col-xs-7">
{#                    <input type="text" name="code" class="form-control" placeholder="请输入图片验证码"required="" id="id_code">#}
{#                    <span style="color: red;"></span>#}
                    {
   
   { form.code }}
                    <span style="color: red">{
   
   { form.code.errors.0 }}</span>
                </div>
                <div class="col-xs-5">
                    <img id="image_code" src="image/code/" style="width: 125px;">
                </div>
            </div>
        </div>
  • auth.py middleware
    image-20221112160826070

  • login.py

Core code 53-58

from django.shortcuts import render, redirect, HttpResponse
from app1.utils.encrypt import md5
from app1 import models
from app1.utils.code import check_code
from io import BytesIO


# 使用form做
from django import forms
from app1.utils.bootstrap import BootStrapForm


class LoginForm(BootStrapForm):  # Form需要自定义字段,
    username = forms.CharField(
        label="用户名",
        widget=forms.TextInput(attrs={
    
    "class": "form-control"})  # 定义标签
    )
    password = forms.CharField(
        label="密码",
        widget=forms.PasswordInput(attrs={
    
    "class": "form-control"}, render_value=True)  # 密码标签
    )
    code = forms.CharField(
        label="验证码",
        widget=forms.TextInput,
        required=True
    )

    # 钩子函数,给密码加密,从而可以使数据与后端密码比较
    def clean_password(self):
        pwd = self.cleaned_data.get("password")
        return md5(pwd)


# # ModelForm则是从数据库中拿字段,去数据库拿安全性差些
# class LoginModelForm(forms.ModelForm):
#     class Meta:
#         model = models.VIP
#         fields = ['username', 'password']

"""
登录
"""
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():
        # form.cleaned_data获取前端用户输入的值(用户名和密码、验证码)
        # print(form.cleaned_data)  # 验证成功,获取到的用户名和密码,因为form与modelForm不同,form不与数据库关联不可以直接写入数据库

        # 验证码校验,并使用pop()把code验证码的值剔除掉,从而保证其不会被存储入数据库
        user_input_code = form.cleaned_data['code']  # 用户填写的验证码
        code = request.session.get('image_code', "")
        if code == user_input_code:
            form.add_error("code", "验证码错误")
            return render(request, 'login.html', {
    
    'form': form})

        # 去数据库校验用户名和密码是否正确
        # 因为上面有个钩子函数,给form.cleaned_data方法加密了,所以这个获取的也是加密的密码
        first = models.VIP.objects.filter(**form.cleaned_data).first()
        # first = models.VIP.objects.filter(username="xxx", password="xxx").first()  ,如上代替具体见搜索手机号详解,只有字典可以这么用

        if not first:
            form.add_error("password", "用户密码错误")  # 给password字段主动添加错误,password从上面18行创建字段表单获取的
            # 存储session到数据库django_session表中自动创建的,也就是用户cookie的凭证,使用cookie的时候得用session来辨别用户
        request.session["info"] = {
    
    'username': first.username}  # 获取用户id和username存储到session中,来当用户凭证,这里只存储username

        # 设置session存活时间,7天内免登录
        request.session.set_expiry(60 * 60 * 24 * 7)
        return redirect("/VIP/list/")
    return render(request, 'login.html', {
    
    'form': form})

"""
验证码
"""
def image(request):
    # 调用pillow函数,生成图片
    img, code_string = check_code()
    # 将生成的验证码写入session中,方便后续用于校验
    request.session['image_code'] = code_string
    # 给Session设置60秒超时,即验证码有效时间只有60s
    request.session.set_expiry(60)

    stream = BytesIO()  # 在内存中创建一个文件
    img.save(stream, 'png')  # 将生成的img文件写入,文件中
    return HttpResponse(stream.getvalue())

9. Ajax asynchronous request

Send partial request

1. Ajax sends get request

  • Front-end page
{% extends 'layout.html' %}

{% block content %}
    <div class="container">
        <h1>任务管理</h1>

        <h3>示例1</h3>
        <input id="btn1" type="button" class="btn btn-prima  ry" value="点击" onclick="clickMe();"/>

    </div>
{% endblock %}
{% block js %}
<script type="text/javascript">
    function clikMe(){
      
      
        $.ajax({
      
      
            url: '/task/ajax/',   // 发送的地址
            type: "get",
            data: {
      
      
                n1: 123,
                n2: 456
                  },
           success: function (res) {
      
      
              console.log(res);
    }
              })
        }
    </script>
 {% endblock %}
$.ajax({
    url: '/task/ajax/',   // 发送的地址
    type: "get",
    data: {
        n1: 123,
        n2: 456
    },
    success: function (res) {
        console.log(res);
    }
})
 {% endblock %}
  • The backend receives the request
from django.shortcuts import render, HttpResponse

def task_ajax(request):
    print(request.GET)
    return HttpResponse("成功了")
  • urls.py
 # ajax请求
    path('ajax/task/', ajax.task_ajax)

2. Ajax sends post request

  • Front-end page
{% extends 'layout.html' %}

{% block content %}
    <div class="container">
        <h1>任务管理</h1>

        <h3>示例1</h3>
        <input id="btn1" type="button" class="btn btn-prima  ry" value="点击" onclick="clickMe();"/>

    </div>
{% endblock %}
{% block js %}
<script type="text/javascript">
    function clikMe(){
      
      
        $.ajax({
      
      
            url: '/task/ajax/',  //发送的地址
            type: "get",
            data: {
      
      
                    n1: 123,
                    n2: 456
                   },
            success: function (res) {
      
      
                       console.log(res);
                                     }
              })
        }
    </script>
 {% endblock %}
  • rear end
from django.shortcuts import render, HttpResponse
from django.views.decorators.csrf import csrf_exempt


@csrf_exempt
def task_ajax(request):
    print(request.GET)
    print(request.POST)
    return HttpResponse("成功了")
  • urls.py
 # ajax请求
    path('ajax/task/', ajax.task_ajax)

3. Binding events

  • front end
{% extends 'layout.html' %}


{% block content %}
    <div class="container">
        <h1>任务管理</h1>

        <h3>示例1</h3>
        <input id="btn1" type="button" class="btn btn-primary" value="点击"/>

    </div>
{% endblock %}

{% block js %}
    <script type="text/javascript">
        $(function () {
      
      
            // 页面框架加载完成之后代码自动执行
            bindBtn1Event();

        })

        function bindBtn1Event() {
      
      
            $("#btn1").click(function () {
      
        // btn1是表单id
                $.ajax({
      
      
                    url: '/task/ajax/',
                    type: "post",
                    data: {
      
      
                        n1: 123,
                        n2: 456
                    },
                    success: function (res) {
      
      
                        console.log(res);
                    }
                })
            })
        }

    </script>
{% endblock %}

4. Return value of ajax

The return value is in json format

{% extends 'layout.html' %}


{% block content %}
    <div class="container">
        <h1>任务管理</h1>

        <h3>示例1</h3>
        <input id="btn1" type="button" class="btn btn-primary" value="点击"/>

    </div>
{% endblock %}

{% block js %}
    <script type="text/javascript">
        $(function () {
      
      
            // 页面框架加载完成之后代码自动执行
            bindBtn1Event();

        })

        function bindBtn1Event() {
      
      
            $("#btn1").click(function () {
      
      
                $.ajax({
      
      
                    url: '/task/ajax/',
                    type: "post",
                    data: {
      
      
                        n1: 123,
                        n2: 456
                    },
                    dataType: "JSON",  // 将后端返回数据转换成json对象格式,json数据在python中是字典(键值对)
                    success: function (res) {
      
      
                        console.log(res);
                        console.log(res.status); //通过json数据对象键获取值
                        console.log(res.data);
                    }
                })
            })
        }

    </script>
{% endblock %}
  • rear end
from django.shortcuts import render, redirect, HttpResponse
from django.views.decorators.csrf import csrf_exempt
import json
from django.http import JsonResponse


def task_list(request):
    """ 任务列表 """
    return render(request, "task_list.html")


@csrf_exempt
def task_ajax(request):
    print(request.GET)
    print(request.POST)

    data_dict = {
    
    "status": True, 'data': [11, 22, 33, 44]}
    return HttpResponse(json.dumps(data_dict))  # 将数据返回去前端页面
    # return JsonResponse(data_dict)  # django带的方法可以简化如上返回代码,需要导包
  • urls.py
 # ajax请求
    path('ajax/task/', ajax.task_ajax)

5. Test code

  • task.html
{% extends 'layout.html' %}


{% block content %}
        <hr/>
        <h1>Ajax学习</h1>
        <h3>示例2</h3>
        <input type="text" id="txtUser" placeholder="姓名">
        <input type="text" id="txtAge" placeholder="年龄">
        <input id="btn2" type="button" class="btn btn-primary" value="点击"/>

        <h3>示例3</h3>
        <form id="form3">
            <input type="text" name="user" placeholder="姓名">
            <input type="text" name="age" placeholder="年龄">
            <input type="text" name="email" placeholder="邮箱">
            <input type="text" name="more" placeholder="介绍">
        </form>
        <input id="btn3" type="button" class="btn btn-primary" value="点击"/>

    </div>
{% endblock %}

{% block js %}
    <script type="text/javascript">
        $(function () {
      
      
            // 页面框架加载完成之后代码自动执行
            bindBtn1Event2();

            bindBtn1Event3();

        })

        function bindBtn1Event2() {
      
      
            $("#btn1").click(function () {
      
      
                $.ajax({
      
      
                    url: '/task/ajax/',
                    type: "post",
                    data: {
      
      
                        user: $("#textUser").val(),  //jquery获取表单的值
                        age: $("textAge").val()
                    },
                    dataType: "JSON",  // 将后端返回数据转换成json对象格式,json数据在python中是字典(键值对)
                    success: function (res) {
      
      
                        console.log(res);
                        console.log(res.status); //通过json数据对象键获取值
                        console.log(res.data);
                    }
                })
            })
        }

        function bindBtn1Event3() {
      
      
            $("#btn3").click(function () {
      
      
                $.ajax({
      
      
                    url: '/task/ajax/',
                    type: "post",
                    data: $("#from3").serialize(),  //serialize()将表单所以值传入后端
                    dataType: "JSON",  // 将后端返回数据转换成json对象格式,json数据在python中是字典(键值对)
                    success: function (res) {
      
      
                        console.log(res);
                        console.log(res.status); //通过json数据对象键获取值
                        console.log(res.data);
                    }
                })
            })
        }

    </script>
{% endblock %}
  • ajax.py
from django.shortcuts import render, redirect, HttpResponse
from django.views.decorators.csrf import csrf_exempt
import json
from django.http import JsonResponse


def task_list(request):
    """ 任务列表 """
    return render(request, "task_list.html")


@csrf_exempt
def task_ajax(request):
    print(request.GET)
    print(request.POST)

    data_dict = {
    
    "status": True, 'data': [11, 22, 33, 44]}
    return HttpResponse(json.dumps(data_dict))  # 将数据返回去前端页面
    # return JsonResponse(data_dict)  # django带的方法可以简化如上返回代码,需要导包
  • urls.py
# ajax请求,注意导入ajax.py包,views文件夹中
    path('ajax/task/', ajax.task_ajax),
    path('ajax/list/', ajax.task_list),

10. Review and sorting out

Review of knowledge points:

  • Install Djangox

    pip install django
    
  • Create a Django project

    >>> django-admin startproject mysite
    

    Note: Pycharm can create. If created with Pycharm, remember to delete the DIR templates in settings.py.

  • Create app & register

    >>>python manage.py startapp app01
    >>>python manage.py startapp app02
    >>>python manage.py startapp app03
    
    INSTALLED_APPS = [
        ...
        'app01.apps.App01Config'
    ]
    

    Note: Otherwise, when writing classes in models.py under the app, the table cannot be created in the database.

  • Configure the static file path & template path (place it in the app directory).

    • static、templates、utils
  • Configure database related operations (MySQL)

    • Third-party module (django3 version)

      pip install mysqlclient
      
    • First go to MySQL to create a database yourself.

      mysql -u root -p
      CREATE DATABASE djangodemo3 CHARACTER SET utf8 COLLATE utf8_general_ci;  utf-8后面是排列规则
      
    • Configure database connection settings.py

      DATABASES = {
              
              
          'default': {
              
              
              'ENGINE': 'django.db.backends.mysql',
              'NAME': 'djangoDemo3',  # 数据库名字
              'USER': 'root',
              'PASSWORD': '1234',
              'HOST': '127.0.0.1',  # 哪台机器安装了MySQL
              'PORT': 3306,
          }
      }
      
    • Written in models.py under app

      from django.db import models
      
      
      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
      
          
      class Department(models.Model):
          """ 部门表 """
          title = models.CharField(verbose_name='标题', max_length=32)
      
          def __str__(self):
              return self.title
      
    • Execute two commands:

      >>>python manange.py makemigrations
      >>>python manange.py migrate
      
  • In urls.py, routing (correspondence between URL and function).

  • In views.py, view function, write business logic.

  • templates directory, write HTML templates (including template syntax, inheritance, {% static 'xx'%})

  • ModelForm & Form components, we develop the add, delete, modify and check function.

    • Generate HTML tags (generate default values)
    • Request data for verification.
    • Save to database (ModelForm)
    • Get error message.
  • Cookies, Sessions, and user login information are saved.

  • Middleware, based on middleware to implement user authentication, based on:process_request.

  • ORM operations

    models.User.objects.filter(id="xxx")
    models.User.objects.filter(id="xxx").order_by("-id")
    
  • Pagination component.

Guess you like

Origin blog.csdn.net/tyx2985491138/article/details/131528972