Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb

Django

本套博客基于银角大王武沛齐的django全栈开发视频编写:传送门
如有错误或改进欢迎大家评论、私信交流指正

一、初识Django

1、Djang的安装

  • 在windos命令窗口

win+r键,输入cmd

pip install django
  • 效果:

Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第1张图片

2、Django项目

①创建Django项目

  • 使用Pycham创建
Ⅰ、使用模板
  • 主页面点击左上角File->newProject
Ⅱ、配置选项

Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第2张图片

Ⅲ、成功效果

Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第3张图片

②项目文件

Ⅰ、默认文件介绍
Demo1
├── manage.py         【项目的管理,启动项目、创建app、数据管理】【不需要修改】【***常常用***】
├── templates          【存放html静态页面,后期手动创建】
├── static              【图片、css、js、plugins(插件)等前端样式资源,后期手动创建】
└── Demo1
    ├── __init__.py
    ├── settings.py    【项目配置文件】          【***常常修改***】
    ├── urls.py        【URL和函数的对应关系】【***常常修改***】
    ├── asgi.py        【接收网络请求】【不要动】
    └── wsgi.py        【接收网络请求】【不要动】

③创建App文件

  • 注意:我们开发比较简洁,用不到多app,一般情况下,项目下创建1个app即可。
Ⅰ、终端创建
  • 语法
python manage.py startapp app文件名称
  • 在当前项目目录下执行manage.py文件创建App
Ⅱ、app文件结构
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、快速上手

①项目注册app

Ⅰ、在项目文件夹下Settings.py文件中
  • app地址格式
'app文件名.apps.类名(apps.py文件中)'
  • 类名

image-20221023181313400

  • 填写信息

Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第4张图片

②urls.py编写url和函数信息

Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第5张图片

③views.py编写视图函数

  • 服务器输出层

Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第6张图片

④启动项目

Ⅰ、命令行启动
  • 端口号可以省略默认8000
python manage.py runserver 8080
  • 关闭
ctrl+c
Ⅱ、PyCham启动

image-20221023185327679

⑤访问页面

在浏览器访问,前面127.0.0.1.8080 是默认的,而index是要访问的页面和执行函数路径,具体见配置

Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第7张图片

4、访问静态页面

  • 与快速上手步骤无异只是修改了几处位置

①创建html静态页面

  • 在app文件目录下创建一个文件夹templates,里面放置html静态页面

  • 选中templates文件夹,右击new—File—Html,然后取文件名user_list

  • 效果:

Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第8张图片

②编写视图函数

  • app文件夹下views.py文件

Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第9张图片

④编写url配置文件

  • Demo1项目文件夹下urls.py文件
    Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第10张图片

⑤访问页面

  • 启动

  • 访问效果:
    Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第11张图片

⑥总结

  • 访问静态页面返回方法要使用:render()并且要导包
# 调用函数访问静态页面
def user_list(request):
    return render(request,"user_list.html")
  • 普通的直接使用HttpResponse()方法
def index(request):
    return HttpResponse("欢迎使用Django")

⑥拓展templates

  • 页面是默认到App文件目录templates文件夹中找html文件的,但是可以配置让其到项目目录找

  • 添加方法:

os.path.jion()

5、项目文件管理

①静态页面

Ⅰ、存放路径
  • app文件—templates文件夹
  • 文件夹名称必须为templates不可以修改
    Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第12张图片
Ⅱ、拓展(修改存放路径)
  • 页面是默认到App文件目录templates文件夹中找html文件的,但是可以配置让其到项目目录找,可以查看
    Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第13张图片

  • 添加方法

os.path.jion()

②静态文件

  • App目录下,名称必须为static文件

  • 图片、css、js、plugins(插件)等前端样式资源
    Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第14张图片

6、Django模板语法

①引用文件

Django独特资源路径
  • 通过项目配置文件Settings.py指定文件查找路径,占位符引用资源
  • 作用:

防止以后做项目需要改文件路径,可以直接通过配置文件修改

  • 语法:
<img src="{% 配置文件指定的路径 '文件路径' %}">
  • 效果:
<img src="{%  static 'img/img.pong' %}">
  • 文件所在路径:
    Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第15张图片

  • 配置文件:
    Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第16张图片

②返回数据

Ⅰ、render对象
  • 说明

  • 导包

from django.shortcuts import render
  • 使用
def user_list(request):
    return render(request,"user_list.html")
Ⅱ、HttpResponse对象
  • 说明

  • 导包

from django.shortcuts import HttpResponse
  • 使用
def index(request):
    return HttpResponse("欢迎使用Django") # 结果输出到访问的页面

③占位符开发

  • 使用占位符让python代码可以在html文件中使用
{% python代码 %}
  • views.py文件

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文件
DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
head>
<body>

<div>{{ n1 }}}div>
<div>{{ n2 }}}div>

<div>{{ n2.0 }}div>
<div>{{ n2.2 }}div>

<div>
    {% for aaa in  n2 %}
    <span>aaaspan>
    {% endfor %}
div>

{{ n3 }}
{{ n3.name }}
{% for foo in n3.foo %}
   <li>{{ foo }}li>
{% endfor %}

{% if n1 == "大胖子" %}
    <h1>h1>
{% else %}
    <h1>h1>
{% endif %}

body>
html>

④案例:把python程序的数据输出到页面上

7、请求和响应

①请求

Ⅰ、获取请求方式

获取前端的请求方式

request.method

Ⅱ、获取get请求传递的值

返回值是字典

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

Ⅲ、获取post请求

request.POST

②响应

  • HttpResponse()
  • render()
Ⅰ、返回字符串
  # [响应]将字符串返回给请求者
  return HttpResponse("返回内容")
Ⅱ、返回一个页面,并返回模板变量
return render(request, 'url_date.html', {"uuu": "大胖子"})
  • html文件
DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
head>
<body>
{{ uuu }}  
body>
html>
Ⅲ、重定向
  • 跳转页面
 # 重定项,访问次函数方法就会跳转页面
 return redirect("https://blog.csdn.net/tyx2985491138?type=blog")

8、用户登录案例

  • views.py文件
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文件
from django.urls import path
from app01 import views # 导入app01中views.py文件(书写函数的文件夹)
urlpatterns = [
    
# 用户登录案例
    path('login/', views.login_a),
]
  • html文件
DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户登录title>
head>
<body>

<h1>用户登录h1>
<form method="post" action="/login/">
    {% csrf_token %}  
    <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、总结

二、操作数据库

  • 实现技术使用的是orm操作数据库

1、快速入门

①py原生代码连接数据库

  • pymysql连接数据库
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连接数据库

  • django的内部框架,用来简化pymsql连接数据库
  • 可以帮我们:
    • 创建、修改、删除数据库中的表 【无法创建数据库
    • 操作数据库不用写sql语句
Ⅰ、安装第三方软件包
  • 安装mysqlclient
pip install mysqlclient
  • 报错解决方法

  • 文件地址:https://wwt.lanzouy.com/ieVdA0es8rad 密码:evzg

  • mysqlclient安装失败解决方法](http://t.csdn.cn/UetPe)

Ⅱ、创建数据库
Ⅰ、登录mysql
  • 打开总结电脑的终端
  • 登录到mysql
 mysql -u -root -p    
 或者:mysql -h localhost -u root -p
Ⅱ、创建数据库
CREATE DATABASE django;
  • 效果
    image-20221029163034942

    Ⅲ、修改setting.py配置文件

第78行

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

③操作数据库

Ⅰ、创建表

  • models.py文件中:
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)
  • 自动生成的sql语句
"""
   生成的sql语句
"""
    # 写了如上的类dajngo会自动生成如下sql语句,表面是app名+类名
create table app01_userinfo(
    id bigint auto_increment primary key,
    name VARCHAR(32),
    password VARCHAR(64)
    age int
)
  • 执行命令

    生成sql

python manage.py makemigrations
python manage.py migrate  
  • 效果
    Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第17张图片
Ⅱ、删除表
  • 将Role表删掉

  • 在创建表的基础上把models.py文件中,要删除的表的类注释或删除掉即可

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)
  • 执行代码
python manage.py makemigrations
python manage.py migrate  
Ⅲ、修改表
  • 在创建表的基础上把models.py文件修改即可
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,可以为空
  • 然后再执行代码
python manage.py makemigrations
python manage.py migrate  

2、增删改查

①添加数据

Ⅰ、控制台添加
  • 语法

    • create()方法
    • 在models.py文件中
    类名或表名.objects.create(字段名='数据')
    
  • 列:

Department.objects.create(title='销售部')
UserInfo.objects.create(name="大胖子",password="110110110",age=3,data=172)
  • 执行代码:
python manage.py makemigrations
python manage.py migrate  
Ⅱ、整合前端添加数据
  • views.py文件
from app01 import models
def orm(request):
    # 在Department文件中执行
    models.Department.objects.create(title="大胖子")
    models.Department.objects.create(title="胡图图")
    return HttpResponse("成功")
  • urls.py文件
# 测试orm添加数据,访问orm后端执行sql
    path('orm/',views.orm())

②删除数据

Ⅰ、控制台删除
  • 语法delete()
# 删除数据
UserInfo.objects.all(id=3).delete()   # 删除UserInof表中id为3的数据
Department.objects.all().delete()   # 删除全部

③获取数据

  • 语法

    类名或表名.objects.all()   # 返回值是列表里面封装了对象,每行的对象。每一行数据都是一个对象
    
    • 获取全部 all()
# 获得数据
data_list = UserInfo.objects.all()   # 返回值是个列表,封装了每一行的对象
for obj in data_list:   # 使用for循环遍历,每行的对象并取出每行表的数据
    print(obj.id,obj.name,obj.password,obj.age,obj.data)
  • 指定条件获取filter()
    • first():获取列表第一个对象
obj = UserInfo.objects.filter(id=1).first()  # 直接返回一个对象
print(obj.id,obj.name,obj.password,obj.age,obj.data)

④修改数据

  • update()
UserInfo.objects.filter(name="大胖子").update(age=999)

3、综合案例:用户管理

  • 创建app
python manage.py startapp app1

三、Django开发

  • 员工管理系统,基础
  • Demo2

1、项目初始化

①、删除多余的代码

Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第18张图片

②、创建app

pycharm项目终端中

python manage.py startapp app1

③、注册app

填写的是路径

'app01.apps.App01Config'

Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第19张图片

2、编写数据库

①、设计表结构

Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第20张图片

②、创建表

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)

③、创建数据库

  • 打开命令终端
win+r  ,输入cmd
  • 登录到mysql
mysql -h localhost -u root -p
  • 创建数据库djangoDemo2,并设置编码UTF-8
 create database djangoDemo2 DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
  • 查看数据库
show databases;

④、修改Settings.py配置文件

  • 将原有的DATABSES全部注释掉,77行左右直接更换如下:注意修改数据库名、用户名、密码
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'djangoDemo2',  # 数据库名字
        'USER': 'root',
        'PASSWORD': '1234',
        'HOST': '127.0.0.1',  # 哪台机器安装了MySQL
        'PORT': 3306, 
    }
}

⑤、django命令生成表

python manage.py makemigrations
python manage.py migrate

⑥、添加数据

控制台添加

  • 选择数据库
USE djangodemo2
  • 添加数据
insert into app_01 app1_department(title) values("销售部"),("IT部");

3、导入静态文件

  • 解压放入app01目录下

    • static文件:https://wwt.lanzouy.com/iFXts0eu1vre

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

4、部门管理功能

注意导包,这个项目后面添加的功能不会把导包写出来,只会书写前端没导过的包,从而循循渐进

①、查询

  • views.py文件
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文件
    Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第21张图片

②、新增

  • 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

无修改,使用即可

③、删除

  • 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
    Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第22张图片

④、编辑

  • 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//edit/', views.depart_edit),
  • depart.html
    Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第23张图片

  • depart_list.html
    Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第24张图片

5、django的html继承

  • 继承语法
{% extends 'layout.html' %}   
{% block content %}
    这里写html页面样式,子类自己独有的
{% endblock %}
  • 作用

可以显示父类的所有页面,并在其基础上进行修改,从而实现自己独特的页面,避免了重复的代码

  • 父类.html
<h1>这个是父类的,子类继承后也会有h1>
<div>
    {% block content %}{% endblock %}  
div> 
  • 子类.html
{% extends '父类.html' %} 
{% block content %}
    
{% endblock %}

四、员工表的管理功能

①、执行sql让数据库有数据

登录数据库: win+r ,cmd 。mysql -u root -p

选择数据库:USE dangodemo2

  • 执行sql添加数据
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);

②、查询

  • 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
    Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第25张图片

  • obj.get_gender_display()

    • 创建员工表时给其定义了,存储数据库1代表是男,2代表是女。这个方法意思是获取规则,从而更好的显示在数据库

    • 员工表:Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第26张图片

    • 因为存储在数据库的数据是1和2

  • **obj.depart.title **

    • 因为外键去获取另一个表的值,title是部门表的部门名称,通过外键查询

    • 员工表:Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第27张图片

  • 效果:
    Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第28张图片

  • 员工表在数据库的样子
    Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第29张图片

③、添加(原始方法)

  • 原始方法:麻烦
- 用户提交数据没有校验
- 错误,页面上不能显示错误提示	
- 页面上每一个字段都需要我们写一遍
- 关联的数据,需要我们手动循环一个一个获取
  • 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 %}
  • 效果:
    Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第30张图片

④、组件

  • Form组件(小简便)
  • ModelForm组件(最简便)
Ⅰ、From组件

简化重复书写表单

  • 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.user }}
    {{ form.pwd }}
    {{ form.email }}
    
form>

使用循环加工

<form method="post">
    {% for field in form%}
    	{{ field }}
    {% endfor %}
    
form>
Ⅱ、ModeForm组件(推荐)

再Form组件的基础上进一步简化

  • 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 %}
    
form>

⑤、ModelForm组件实现添加

  • 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设置中文

    • LANGUAGE_CODE = 'zh-hans' 
      

    Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第31张图片

  • 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 %}
                    
                    
                    
                    
                    
                    {% for field in form %}
                        <div class="form-group">
                            <label>{{ field.label }}label>
                            {{ field }}
                            
                            <span style="color: red;">{{ field.errors.0 }}span> 
                        div>
                    {% endfor %}

                    <button type="submit" class="btn btn-primary">提 交button>
                form>
            div>
        div>
    div>
{% endblock %}
  • 效果:
    Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第32张图片

  • 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)

⑥编辑

使用组件

Ⅰ、思路
  • 点击编辑用户,跳转页面并携带需要编辑的那一行的id get
  • 根据携带过来的id显示一些默认信息(从数据库获取) get
  • 提交:post
    • 错误提示、数据校验
    • 数据库更新数据
Ⅱ、实现
  • 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//edit/', views.user_edit),
  • user_edit.html

需要自己创建

{% 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 %}
                    
                    
                    
                    {% for field in form %}
                        <div class="form-group">
                            <label>{{ field.label }}label>
                            {{ field }}

                            
                            <span style="color: red;">{{ field.errors.0 }}span>
                        div>
                    {% endfor %}
                    <button type="submit" class="btn btn-primary">提 交button>
                form>
            div>
        div>

    div>
{% endblock %}
  • user_list.html

    修改一些内容
    Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第33张图片

  • 效果
    Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第34张图片

⑦删除

  • 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
    Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第35张图片

五、靓号管理

①创建表

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)

②靓号列表

  • 将ID、号码、价格、级别(中文)、状态(中文)、操作(编辑和删除)展现出来

  • 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">
            
            <div class="panel-heading">
                <span class="glyphicon glyphicon-th-list" aria-hidden="true">span>
                靓号列表
            div>

            
            <table class="table table-bordered">
                <thead>
                <tr>
                    <th>IDth>
                    <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 %}

④添加

  • 通过正则表达式校验数据

  • 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 }}
                            
                            <span style="color: red;">{{ field.errors.0 }}span>
                        div>
                    {% endfor %}

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

 {% endblock %}

⑤编辑

  • 电话号码不可编辑,但还是要显示到页面上

  • 点击编辑后会携带靓号的id到后端从而修改

  • 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//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 }}

                            
                            <span style="color: red;">{{ field.errors.0 }}span>
                        div>
                    {% endfor %}
                    <button type="submit" class="btn btn-primary">提 交button>
                form>
            div>
        div>

    div>
{% endblock %}

⑥删除

  • views.py
def 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

⑦搜索查询

  • 手机号搜索
Ⅰ、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
    queryset = models.PrettyNum.objects.filter(**data_dict).order_by("-price")  # 按条件查询
    return render(request, 'admin_list.html', {'queryset': queryset, 'search_data': search_data})
Ⅱ、urls.py
  • 不改变
 # 靓号列表主页面
    path('admin/list/', views.admin_list),
Ⅲ、admin_list.html
  • 将原主页修改

  • 搜索框样式是bootstrap:官网https://v3.bootcss.com/ 样式包(bootstrap-3.4.1):https://wwt.lanzouy.com/iYMpj0f7fgze

  • 导入样式包(bootstrap-3.4.1)

    <script src="{% static 'plugins/bootstrap-3.4.1/js/bootstrap.js' %}"></script>
    
Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第36张图片
  • 代码
<div style="float: right;width: 300px">
    <form method="get">
        <div class="input-group">
            <input type="text" name="q" class="form-control" placeholder="搜索号码"
                   value="{{ search_data }}">
            
            <span class="input-group-btn">
                <button class="btn b tn-default" type="submit">Go!button>
        span>
    form>
div>

⑧分页查询

  • 给数据库添加300条数据

写在views.py的admin_list函数中,访问即可给数据库添加300条数据

# 添加300条数据
    for i in range(300):
        models.PrettyNum.objects.create(mobile="16656678912", price=10, level=1, status=1)
Ⅰ、sql语法:
 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 = '
  • 上一页
  • '
    .format(page - 1) # 当前页减一 else: prev = '
  • 上一页
  • '
    .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 = '
  • {}
  • '
    .format(i, i) else: elc = '
  • {}
  • '
    .format(i, i) page_str_list.append(elc) # 将添加进列表中 # 下一页 if page < total_page_count: prev = '
  • 下一页
  • '
    .format(page + 1) # 当前页加1 else: prev = '
  • 下一页
  • '
    .format(total_page_count) # 总页码 page_str_list.append(prev) # 尾页 one = '
  • 尾页
  • '
    .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

    不做更改

    Ⅲ、admin_list.html
    
            <ul class="pagination">
                {#        <li><a href="?page=1">1a>li>#}
                {#        <li><a href="?page=2">2a>li>#}
                {#        <li><a href="?page=3">3a>li>#}
                {#        <li><a href="?page=4">4a>li>#}
                {#        <li><a href="?page=5">5a>li>#}
                {{ page_string }}   
                
                <li>
                    <form method="get">
                        <div class="input-group">
                            <input type="text" name="p" class="form-control" placeholder="搜索号码"
                                   value="{{ search_data }}">
                            
                            <span class="input-group-btn">
                                <button class="btn b tn-default" type="submit">Go!button>
                        span>
                        div>
                    form>
                li>
            ul>
    
    Ⅳ、效果:

    Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第37张图片

    ⑨、使用封装简化分页代码(封装分页)

    Ⅰ、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
    • 在app1中创建utils文件夹里面创建paginaion.py

    • 将页码生成、数据获取、前端页码生成封装到pagination.py文件中Pagination类中

    """
    自定义分页组件
    """
    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('
  • 首页
  • '
    .format(self.query_dict.urlencode())) # 上一页 if self.page > 1: self.query_dict.setlist(self.page_param, [self.page - 1]) prev = '
  • 上一页
  • '
    .format(self.query_dict.urlencode()) else: self.query_dict.setlist(self.page_param, [1]) prev = '
  • 上一页
  • '
    .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 = '
  • {}
  • '
    .format(self.query_dict.urlencode(), i) else: ele = '
  • {}
  • '
    .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 = '
  • 下一页
  • '
    .format(self.query_dict.urlencode()) else: self.query_dict.setlist(self.page_param, [self.total_page_count]) prev = '
  • 下一页
  • '
    .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('
  • 尾页
  • '
    .format(self.query_dict.urlencode())) search_string = """
  • """
    page_str_list.append(search_string) page_string = mark_safe("".join(page_str_list)) return page_string
    Ⅲ、效果

    Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第38张图片

    六、拓展功能(1)

    1、时间插件

    • 插件链接:https://wwt.lanzouy.com/imuOd0fe3opc

    • 用于美化、简化入职时间填写,可以直接通过点击xuan’ze

    2、分模块开发

    • 将views.py中用户管理、部门管理、靓号管理分为三个py文件,代码不用改,只需改urls.py中函数路径

    • views文件夹
      Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第39张图片

    • urls.py

    重点在导包,和函数路径

    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//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//edit/', user.user_edit),
        # 删除
        path('user/delete/', user.user_delete),
    
        # 靓号管理
        # 靓号列表主页面
        path('admin/list/', admin.admin_list),
        # 添加
        path('admin/add/', admin.admin_add),
        # 编辑
        path('admin//edit/', admin.admin_edit),
        # 删除
        path('admin/delete/', admin.admin_delete)
    
    ]
    

    七、管理员

    1、创建数据库

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

    2、管理员列表

    • VIP.py

    在views文件夹中创建VIP.py文件

    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 }}">   
                            
                            <span class="input-group-btn">
                                <button class="btn b tn-default" type="submit">搜索查询button>
                        span>
                        div>
                    form>
                div>
            div>
            <div class="panel panel-default">
                
                <div class="panel-heading">
                    <span class="glyphicon glyphicon-th-list" aria-hidden="true">span>
                    管理员账户列表
                div>
    
                
                <table class="table table-bordered">
                    <thead>
                    <tr>
                        <th>IDth>
                        <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、添加

    • 重点:密码校验

    • VIP.py

    views文件夹下

    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
      • 因为添加的页面文件都差不多一样,所以可以使用一个文件,只是在需要不同的地方给其自定义
    
    {% 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 }}
                                
                                <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
      • 自定义的密码校验工具类,放在app01/utils/下
      • 密码和确认密码比较也是使用加密后的密码进行比较
      • 原理是将密码嵌入到django配置文件随机生成的字符串中,具体见第8行代码
    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、编辑

    • VIP.py

    views文件夹子文件

    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//edit/', VIP.VIP_edit),
    
    • change.html
      • 和如上添加管理员使用同一个html,只是有一些区别在views.py文件(VIP.py)中

    5、删除

    • 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),
    
    • 注意把管理员的主页面按钮路径改一下

    6、重置密码

    • 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//reset/', VIP.VIP_reset),
    
    • BootStrap.py

    自定义的工具包,因为重置密码使用的是form而不是ModelForm所以使用继承改装一下

    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

    八、拓展功能(2)

    1、管理员登录

    • 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>Titletitle>
        <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>
    
    • cookie和session
    request.session["info"] = {'id': filter.id, 'username': first.username}  # 获取用户id和username存储到session中,来当用户凭证
            request.session["info"] = first.username   # 这里只存储个username
    

    2、校验用户登录

    ①校验登录

    原理是通过session来获取cookie,如果没有cookie就没有登录过

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

    ②中间件校验

    因为中间键的使用所以,不需要每个views.py中都使用如上代码校验登录。只需要定义一个中间件即可,函数文件中代码不需要改变

    • 创建中间件

    在app1下创建个文件夹middleware,再创建个auth.py。里面代码如下

    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/')
    
    • 注册中间件

    setting.py文件中,每个类是一个中间件

    Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第40张图片

    3、注销

    • 原理

    删除cookie即可,因为没有cookie需要重新登录

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

    将html的注销标签的执行函数地址改一下

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

    4、显示当前用户名

    随着用户改变用户名也会跟着自动改变

    • 获取当前用户的账号和密码
    # 通过session来获取当前用户的账号和密码
        info = request.session.get("info")   # 获取session的info字典的用户cookie凭证
        # 取值,存储在session中用户输入的账号和密码
        info["id"]   
        info["username"]
    
    • 应用在前端
    {{ request.session.info.username }}
    
    • 效果:

    Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第41张图片

    • 获取的是login.py文件夹存储入session的值:

    image-20221112115758287

    5、图片验证码

    ①pillow插件

    自动生成验证码图片插件

    Ⅰ、安装插件
    pip install pillow
    

    或者到pycham设置中项目-python解释器中安装

    Ⅱ、使用

    参考文献:https://www.cnblogs.com/wupeiqi/articles/5812291.html

    Ⅲ、字体文件

    生成验证码的字体:

    Ⅳ、验证码的生成与校验
    • code.py

    自定义的工具包文件,注意修改第4行的字体路径,这个字体是和app1在同一个目录下

    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中间件
      Django银角大王武沛齐配套视频笔记,python全栈开发、pythonWeb_第42张图片

    • login.py

    核心代码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())
    

    九、Ajax异步请求

    发送局部请求

    1、ajax发送get请求

    • 前端页面
    {% extends 'layout.html' %}
    
    {% block content %}
        <div class="container">
            <h1>任务管理h1>
    
            <h3>示例1h3>
            <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 %}
    
    • 后端接收请求
    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发送post请求

    • 前端页面
    {% extends 'layout.html' %}
    
    {% block content %}
        <div class="container">
            <h1>任务管理h1>
    
            <h3>示例1h3>
            <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 %}
    
    • 后端
    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、绑定事件

    • 前端
    {% extends 'layout.html' %}
    
    
    {% block content %}
        <div class="container">
            <h1>任务管理h1>
    
            <h3>示例1h3>
            <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、ajax的返回值

    返回值是json格式

    {% extends 'layout.html' %}
    
    
    {% block content %}
        <div class="container">
            <h1>任务管理h1>
    
            <h3>示例1h3>
            <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 %}
    
    • 后端
    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、测试代码

    • task.html
    {% extends 'layout.html' %}
    
    
    {% block content %}
            <hr/>
            <h1>Ajax学习h1>
            <h3>示例2h3>
            <input type="text" id="txtUser" placeholder="姓名">
            <input type="text" id="txtAge" placeholder="年龄">
            <input id="btn2" type="button" class="btn btn-primary" value="点击"/>
    
            <h3>示例3h3>
            <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),
    

    十、回顾与梳理

    知识点的回顾:

    • 安装Djangox

      pip install django
      
    • 创建Django项目

      >>> django-admin startproject mysite
      

      注意:Pycharm可以创建。如果用Pycharm创建,记得settings.py中的DIR templates 删除。

    • 创建app & 注册

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

      注意:否则app下的models.py写类时,无法在数据库中创建表。

    • 配置 静态文件路径 & 模板的路径(放在app目录下)。

      • static、templates、utils
    • 配置数据库相关操作(MySQL)

      • 第三方模块(django3版本)

        pip install mysqlclient
        
      • 自己先去MySQL创建一个数据库。

        mysql -u root -p
        CREATE DATABASE djangodemo3 CHARACTER SET utf8 COLLATE utf8_general_ci;  utf-8后面是排列规则
        
      • 配置数据库连接settings.py

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

        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
        
      • 执行两个命令:

        >>>python manange.py makemigrations
        >>>python manange.py migrate
        
    • 在 urls.py ,路由 ( URL 和 函数的对应关系)。

    • 在views.py,视图函数,编写业务逻辑。

    • templates目录,编写HTML模板(含有模板语法、继承、{% static 'xx'%}

    • ModelForm & Form组件,在我们开发增删改查功能。

      • 生成HTML标签(生成默认值)
      • 请求数据进行校验。
      • 保存到数据库(ModelForm)
      • 获取错误信息。
    • Cookie和Session,用户登录信息保存起来。

    • 中间件,基于中间件实现用户认证 ,基于:process_request

    • ORM操作

      models.User.objects.filter(id="xxx")
      models.User.objects.filter(id="xxx").order_by("-id")
      
    • 分页组件。

    你可能感兴趣的:(python,django,笔记)