一、Web应用程序处理流程

  • Web应用程序的本质

    • 接收并解析HTTP请求,获取具体的请求信息
    • 处理本次HTTP请求,即完成本次请求的业务逻辑处理
    • 构造并返回处理结果——HTTP响应
  • Web程序框架的意义

    • 用于搭建Web应用程序
    • 免去不同Web应用相同代码部分的重复编写,只需关心Web应用核心的业务逻辑实现

img

二、Django简介

1. Django简介

  • Django,是用python语言写的开源web开发框架,并遵循MVC设计,劳伦斯出版集团为了开发以新闻内容为主的网站,而开发出来了这个框架,于2005年7月在BSD许可证下发布
  • Django这个名称来源于比利时的爵士音乐家DjangoReinhardt,他是一个吉普赛人,主要以演奏吉它为主,还演奏过小提琴等
  • 由于Django在近年来的迅速发展,应用越来越广泛,被著名IT开发杂志SDTimes评选为2013SDTimes100,位列”API、库和框架”分类第6位,被认为是该领域的佼佼者
  • Django的主要目的是简便、快速的开发数据库驱动的网站。它强调代码复用,多个组件可以很方便的以”插件”形式服务于整个框架
  • Django有许多功能强大的第三方插件,甚至可以很方便的开发出自己的工具包,这使得Django具有很强的可扩展性
  • Django强调快速开发和DRY(DoNotRepeatYourself)原则

2. Django的特点

(1)重量级框架

  • 对比Flask框架,Django原生提供了众多的功能组件,让开发更简便快速

    • 提供项目工程管理的自动化脚本工具
    • 数据库ORM支持(对象关系映射,英语:Object Relational Mapping)
    • 模板
    • 表单
    • Admin管理站点
    • 文件管理
    • 认证权限
    • session机制
    • 缓存

(2)MVT模式

  • MVC程序设计模式(Model-View-Controller):其核心思想是分工、解耦,让不同的代码块之间降低耦合,增强代码的可扩展性和可移植性,实现向后兼容

  • 最早由TrygveReenskaug在1978年提出,是施乐帕罗奥多研究中心(Xerox PARC)在20世纪80年代为程序语言Smalltalk发明的一种软件设计模式,是为了将传统的输入(input)、处理(processing)、输出(output)任务运用到图形化用户交互模型中而设计的。随着标准输入输出设备的出现,开发人员只需要将精力集中在业务逻辑的分析与实现上

  • 后来被推荐为Oracle旗下Sun公司Java EE平台的设计模式,并且受到越来越多的使用ColdFusion和PHP的开发者的欢迎。现在虽然不再使用原来的分工方式,但是这种分工的思想被沿用下来,广泛应用于软件工程中,是一种典型并且应用广泛的软件架构模式。后来,MVC的思想被应用在了Web开发方面,被称为Web MVC框架

  • MVC模式

    • M全拼为Model,主要封装对数据库层的访问,对数据库中的数据进行增、删、改、查操作。
    • V全拼为View,用于封装结果,生成页面展示的html内容。
    • C全拼为Controller,用于接收请求,处理业务逻辑,与Model和View交互,返回结果。

img

  • MVT模式(Django)

    • M全拼为Model,与MVC中的M功能相同,负责和数据库交互,进行数据处理。
    • V全拼为View,与MVC中的C功能相同,接收请求,进行业务处理,返回应答。
    • T全拼为Template,与MVC中的V功能相同,负责封装构造要返回的html。

img

三、Django流程

  • MVT流程

img

1. 创建工程

  • 在使用Flask框架时,项目工程目录的组织与创建是需要我们自己手动创建完成的

  • 在django中,项目工程目录可以借助django提供的命令帮助我们创建

  • 创建工程:django-admin startproject 工程名称

执行后,会多出一个工程名称的新目录,此即为新创建的工程目录

  • 查看创建的工程目录,结构如下

    • 与项目同名的目录
    • settings.py:项目的整体配置文件
    • urls.py:项目的URL配置文件
    • wsgi.py:项目与WSGI兼容的Web服务器入口
    • manage.py:项目管理文件,通过它管理项目

img

  • 运行开发服务器(在开发阶段,为了能够快速预览到开发的效果,django提供了一个纯python编写的轻量级web服务器,仅在开发阶段使用),运行服务器命令:python manage.py runserver ip:端口

    • 设置其他IP访问:将IP加入工程文件settings.py中的ALLOWED_HOSTS列表:ALLOWED_HOSTS = [],运行命令:python manage.py runserver 0:端口

可以不写IP和端口,默认IP是127.0.0.1,默认端口为8000

img

  • 在浏览器中输入网址便可看到效果:127.0.0.1:8000

django默认工作在调式Debug模式下,如果增加、修改、删除文件,服务器会自动重启

img

  • 停止服务器:ctrl+c

2. 创建子应用

  • 在Web应用中,通常有一些业务功能模块是在不同的项目中都可以复用的,故在开发中通常将工程项目拆分为不同的子功能模块,各功能模块间可以保持相对的独立,在其他工程项目中需要用到某个特定功能模块时,可以将该模块代码整体复制过去,达到复用,在Flask框架中也有类似子功能应用模块的概念,即蓝图(Blueprint)
  • Django的视图编写是放在子应用中的
  • 创建子应用模块目录:python manage.py startapp 子应用名称

manage.py为上述创建工程时自动生成的管理文件

  • 执行后,可以看到工程目录中多出了一个子目录,结构如下

    • admin.py:与网站的后台管理站点配置相关
    • apps.py:用于配置当前子应用的相关信息
    • migrations:用于存放数据库迁移历史文件
    • models.py:用户保存数据库模型类
    • tests.py:用于开发测试用例,编写单元测试
    • views.py:用于编写Web应用视图
  • 子应用需要注册安装后才能使用,注册安装子应用:将子应用的配置信息文件添加到工程配置文件settings.py中的INSTALLED_APPS列表中

此步骤需要设置PyCharm的解释器:可以通过which python查看虚拟环境的python脚本路径

img

3. 模块

  • 模块:MVT设计模式中的M(model)

  • 使用Django进行数据库开发的提示 :

    • MVT设计模式中的Model,专门负责和数据库交互,对应models.py
    • 由于Model中内嵌了ORM框架,所以不需要直接面向数据库编程,而是定义模型类, 通过模型类和对象完成数据库表的增删改查
    • ORM框架就是把数据库表的行与相应的对象建立关联,互相转换,使得数据库的操作面向对象

img

  • 使用Django进行数据库开发的步骤 :

    • 定义模型类
      • 优化模型类展示,可以使用__str__魔法函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from django.db import models

# Create your models here.
class BookInfo(models.Model):
name = models.CharField(max_length=10)
# 添加Manage实例的属性对象,以便视图调用
objects = models.Manager()

def __str__(self):
return self.name

class PeopleInfo(models.Model):
name = models.CharField(max_length=10)
age = models.BooleanField()
book = models.ForeignKey(BookInfo,on_delete=models.CASCADE)

遇到的问题

关联外键时遇到错误:TypeError: init() missing 1 required positional argument: ‘on_delete’

解决方案

给关联的模型添加限制:models.ForeignKey(User, on_delete=models.CASCADE)

    • 模型迁移
      1. 生成迁移文件(根据模型类生成创建表):python manage.py makemigrations

img

      1. 执行迁移(根据第一步生成的语句在数据库中创建表):python manage.py migrate

img

    • 操作数据库(默认采用sqlite3数据库来存储数据)
  • 需要pycharm专业版才能看数据库视图(database工具)

  • pycharm社区版可以安装插件:DB Browser,但只能连接数据库,无法打开数据库文件

4. 站点管理

  • 站点:分为内容发布(模块)和公共访问(视图)两部分

    • 内容发布:由网站的管理员负责查看、添加、修改、删除数据
  • Django能够根据定义的模型类自动地生成管理模块:

    1. 管理界面本地化:本地化是将显示的语言、时间等使用本地的习惯
      • 中国大陆地区使用简体中文:LANGUAGE_CODE = 'zh-Hans'
      • 时区使用亚洲/上海时区:TIME_ZONE = 'Asia/Shanghai'
    1. 创建管理员:python manage.py createsuperuser,按提示输入用户名、邮箱、密码
    1. 在应用的admin.py文件中注册模型类:admin.site.register(模型类),注册后可以在站点管理界面方便快速的管理数据

img

5. 视图

  • 视图:MVT设计模式中的V(View),对于Django的设计框架MVT,用户在URL中请求的是视图,视图接收请求后进行处理,并将处理的结果返回给请求者

  • 使用视图时需要进行两步操作

    • 定义视图
      • 视图就是一个Python函数,被定义在应用的views.py中
      • 视图的第一个参数是HttpRequest类型的对象:reqeust,包含了所有请求信息
      • 视图必须返回HttpResponse对象,包含返回给请求者的响应信息
      • 需要导入HttpResponse模块:from django.http import HttpResponse
1
2
3
4
5
from django.http import HttpResponse

# Create your views here.
def index(request):
return HttpResponse('index')
    • 配置URLconf
      • 查找视图的过程 :
        1. 请求者在浏览器地址栏中输入URL, 请求到网站
        2. 网站获取URL信息
        3. 然后与编写好的URLconf逐条匹配
        4. 如果匹配成功则调用对应的视图
        5. 如果所有的URLconf都没有匹配成功,则返回404错误
      • URLconf入口:工程中的settings.py文件,ROOT_URLCONF = 'bookmanager.urls'
      • 需要两步完成URLconf配置
        1. 在项目中定义URLconf
1
2
3
4
5
6
7
8
from django.contrib import admin
from django.urls import include,path
# 固定写法,值为列表
# http://ip:port get和post请求不参与匹配
urlpatterns = [
path('admin/', admin.site.urls),
path('index/',include('index.urls')),
]
        1. 在应用中定义URLconf
1
2
3
4
5
6
7
8
9
from django.urls import path
from index import views

# 一条URLconf包括URL规则、视图两部分
# URL规则:使用正则表达式定义
# 视图:在views.py中定义的视图函数
urlpatterns = [
path('',views.index),
]
  • 视图处理过程

img

img

  • url匹配过程

img

6. 模版

  • 模板:MVT设计模式中的T(Template),在Django中,将前端的内容定义在模板中, 然后再把模板交给视图调用
  • 模板使用步骤
    1. 创建模板
      • 在应用同级目录下创建模板文件夹(固定写法):templates
      • 在templates文件夹下,创建应用同名文件夹
      • 在应用同名文件夹下创建网页模板文件
    1. 设置模板查找路径
      • 找到工程下的settings.py文件
      • 在TEMPLATES变量里设置:'DIRS': [BASE_DIR / 'templates']
    1. 模板接收视图传入的数据
1
2
3
4
def index(request):
context = {'title':'测试数据'}

return render(request,'index/index.html',context)
    1. 模板处理数据
1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="#">{{ title }}</a>
</body>
</html>
  • 视图-模版流程

img

7. 配置文件和静态文件

(1)配置文件

  • BASE_DIR:当前工程的根目录,Django会依此来定位工程内的相关文件,我们也可以使用该参数来构造文件路径

BASE_DIR = Path(file).resolve().parent.parent:表示当前文件相对路径的父级的父级

  • DEBUG:调试模式,创建工程后初始值为True,即默认工作在调试模式下

    • 修改代码文件后,程序会自动重启
    • 程序出现异常时,会向前端显示详细的错误追踪信息(非调试模式下,仅返回Server Error (500))

注意:部署线上运行的Django不要运行在调式模式下,需要修改:DEBUG=FalseALLOW_HOSTS

img

  • 本地语言与时区:Django支持本地化处理,本地化是将显示的语言、时间等使用本地的习惯,初始化的工程默认英语和UTC标准时区

(2)静态文件

  • 项目中的CSS、图片、js都是静态文件,一般会将静态文件放到一个单独的目录中,以方便管理

  • 在html页面中调用时,也需要指定静态文件的路径,Django中提供了一种解析的方式配置静态文件路径,静态文件可以放在项目根目录下,也可以放在应用的目录下

  • 由于有些静态文件在项目中是通用的,所以推荐放在项目的根目录下,步骤如下

    • 在根目录下创建静态文件目录,一般命名为:static
    • 需要在工程下的settings.py配置两个参数
      • STATIC_URL:访问静态文件的URL前缀
      • STATICFILES_DIRS:存放查找静态文件的目录
1
2
3
4
STATIC_URL = 'static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR,'static'),
]

(3)App应用配置

  • 在每个应用目录中都包含了apps.py文件,用于保存该应用的相关信息
  • 在创建应用时,Django会向apps.py文件中写入一个该应用的配置类

img

  • 将此类添加到工程settings.py中的INSTALLED_APPS列表中,表明注册安装具备此配置属性的应用
  • AppConfig.name:表示该配置类是加载的应用,每个配置类必须包含此属性,默认自动生成
  • AppConfig.verbose_name:用于设置该应用的直观可读的名字,此名字在Django提供的Admin管理站点中会显示
1
2
3
4
class IndexConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'index'
verbose_name = '后台相关'

img

四、模型

1. 项目准备

  • 进入指定虚拟环境:workon 虚拟环境名称

  • 进入指定根目录,创建项目:django-admin startproject 项目名

  • 进入工程文件,创建应用:python manage.py startapp 应用名

  • 进入代码编辑环境,打开项目,更换python解释器

  • 安装添加子应用:应用名.apps.配置类名

  • 本地化

    • 设置中文:LANGUAGE_CODE = 'zh-Hans'
    • 设置时区:TIME_ZONE = 'Asia/Shanghai'
  • 准备模板

    • 在根目录下创建templates模板文件夹
    • 设置模版路径:'DIRS': [BASE_DIR / 'templates']
  • 配置项目urls路径:只要不是admin/就算匹配成功,并包含到应用中的urls.py

  • 创建应用urls.py并配置路径:路径中包含booklist/,就调用视图中对应的函数

  • 定义视图:提供数据库信息

  • 进入项目文件,开启项目对应的服务器:python manage.py runserver

  • 浏览器中输入网址:http://127.0.0.1:8000/booklist/

2. 配置

  • 工程目录settings.py中保存了数据库的连接配置信息,Django默认初始配置使用sqlite数据库

sqlite是嵌入式的关系型数据库,主要在移动端使用,属于小型的关系型数据库

  • 使用MySQL数据库首先需要安装驱动程序:pip install PyMySQL
  • 在工程目录__init__.py文件中添加如下代码,让Django的ORM能以mysqldb的方式来调用PyMySQL
1
2
3
import pymysql

pymysql.install_as_MySQLdb()
  • 回到工程目录/settings.py中修改DATABASES配置信息
1
2
3
4
5
6
7
8
9
10
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'HOST':'192.168.241.128',
'PORT':3306,
'USER':'root',
'PASSWORD':'root',
'NAME':'book'
}
}

在MySQL中创建数据库:create database charset=utf8;

3. 定义模型类

  • 模型类被定义在应用的models.py文件中
  • 模型类必须继承自Model类,位于包django.db.models中
1
2
3
4
5
from django.db import models

# Create your models here.
class BookInfo(models.Model):
...
  • 数据库表名

    • 通过db_table指明数据库表名:db_table = 表名
    • 默认表名:小写app应用名_小写模型类名
1
2
3
4
5
6
7
class BookInfo(models.Model):

class Meta:
# 修改表名
db_table = 'bookinfo'
# 在admin站点中显示的名称
verbose_name = '图书'
  • 主键

    • django会为表创建自动增长的主键列,每个模型只能有一个主键列
    • 默认创建的主键列属性为id,可以使用pk代替(primary key)
    • 如果使用选项设置某属性为主键列,django不会再创建自动增长的主键列
  • 属性命名

    • 不能是python、sql的保留关键字
    • 不允许使用连续的下划线,这是由django的查询方式决定的
    • 定义属性时需要指定字段类型,通过字段类型的参数指定选项
      • 语法:属性=models.字段类型(选项)
  • 字段类型

    • AutoField:自动增长的IntegerField,通常不用指定(Django会自动创建属性名为id的自动增长属性)
    • BooleanField:布尔字段,值为True或False
    • NullBooleanField:支持Null、True、False三种值
    • CharField:字符串
      • max_length:表示最大字符个数(必须设置)
    • TextField:大文本字段,一般超过4000个字符时使用
    • IntegerField:整数
    • DecimalField:十进制浮点数
      • max_digits:表示总位数
      • decimal_places:表示小数位数
    • FloatField:浮点数
    • DateField:日期
      • auto_now:表示每次保存对象时,自动设置该字段为当前时间,用于”最后一次修改”的时间戳,它总是使用当前日期,默认为False
      • auto_now_add:表示当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为False
      • auto_now_add和auto_now是相互排斥的,组合将会发生错误
    • TimeField:时间,参数同DateField
    • DateTimeField:日期时间,参数同DateField
    • FileField:上传文件字段
    • ImageField:继承于FileField,对上传的内容进行校验,确保是有效的图片
  • 选项

    • null:数据库范畴如果为True,表示允许为空,默认值是False
    • blank:表单验证范畴如果为True,则该字段允许为空白,默认值是False
    • db_column:字段的名称,如果未指定,则使用属性的名称
    • db_index:若值为True, 则在表中会为此字段创建索引,默认值是False
    • default:默认
    • primary_key:若为True,则该字段会成为模型的主键字段,默认值是False,一般作为AutoField的选项使用
    • unique:如果为True, 这个字段在表中必须有唯一值,默认值是False
    • on_delete:设置外键
    • choices:有序字典
  • 外键

    • 在设置外键时,需要通过on_delete选项指明主表删除数据时,对于外键引用表数据如何处理,在django.db.models中包含了可选常量:
      • CASCADE:级联,删除主表数据时连通一起删除外键表中数据
      • PROTECT:保护,通过抛出ProtectedError异常,来阻止删除主表中被外键应用的数据
      • SET_NULL:设置为NULL,仅在该字段null=True允许为null时可用
      • SET_DEFAULT:设置为默认值,仅在该字段设置了默认值时可用
      • SET():设置为特定值或者调用特定方法
      • DO_NOTHING:不做任何操作,如果数据库前置指明级联性,此选项会抛出IntegrityError异常
    • 使用外键关联的表,django映射到数据库时,会自动在关联表添加一个字段
      • 字段名称:关联字段名称_被关联表主键字段名称
      • 如果外键是在中途添加的,则在运行中会报错,需要自己手动添加关联字段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
from django.db import models

# Create your models here.
# 准备书籍列表信息的模型类
class BookInfo(models.Model):
# 创建字段,字段类型...
name = models.CharField(max_length=20, verbose_name='名称')
pub_date = models.DateField(verbose_name='发布日期',null=True)
readcount = models.IntegerField(default=0, verbose_name='阅读量')
commentcount = models.IntegerField(default=0, verbose_name='评论量')
is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')

class Meta:
db_table = 'bookinfo' # 指明数据库表名
verbose_name = '图书' # 在admin站点中显示的名称

def __str__(self):
"""定义每个数据对象的显示信息"""
return self.name

# 准备人物列表信息的模型类
class PeopleInfo(models.Model):
GENDER_CHOICES = (
(0, 'male'),
(1, 'female')
)
name = models.CharField(max_length=20, verbose_name='名称')
gender = models.SmallIntegerField(choices=GENDER_CHOICES, default=0, verbose_name='性别')
description = models.CharField(max_length=200, null=True, verbose_name='描述信息')
book_id = models.ForeignKey(BookInfo, on_delete=models.CASCADE, verbose_name='图书') # 外键
is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')

class Meta:
db_table = 'peopleinfo'
verbose_name = '人物信息'

def __str__(self):
return self.name

4. 模型迁移

  • 生成迁移文件:python manage.py makemigrations
  • 同步到数据库中:python manage.py migrate

问题描述:执行python manage.py makemigrations 时,提示:No changes detected

解决方案执行python manage.py makemigrations 应用名称

测试数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
insert into bookinfo(name, pub_date, readcount,commentcount, is_delete) values
('射雕英雄传', '1980-5-1', 12, 34, 0),
('天龙八部', '1986-7-24', 36, 40, 0),
('笑傲江湖', '1995-12-24', 20, 80, 0),
('雪山飞狐', '1987-11-11', 58, 24, 0);
insert into peopleinfo(name, gender, book_id_id, description, is_delete) values
('郭靖', 1, 1, '降龙十八掌', 0),
('黄蓉', 0, 1, '打狗棍法', 0),
('黄药师', 1, 1, '弹指神通', 0),
('欧阳锋', 1, 1, '蛤蟆功', 0),
('梅超风', 0, 1, '九阴白骨爪', 0),
('乔峰', 1, 2, '降龙十八掌', 0),
('段誉', 1, 2, '六脉神剑', 0),
('虚竹', 1, 2, '天山六阳掌', 0),
('王语嫣', 0, 2, '神仙姐姐', 0),
('令狐冲', 1, 3, '独孤九剑', 0),
('任盈盈', 0, 3, '弹琴', 0),
('岳不群', 1, 3, '华山剑法', 0),
('东方不败', 0, 3, '葵花宝典', 0),
('胡斐', 1, 4, '胡家刀法', 0),
('苗若兰', 0, 4, '黄衣', 0),
('程灵素', 0, 4, '医术', 0),
('袁紫衣', 0, 4, '六合拳', 0);

5. shell工具和mysql日志

  • shell工具:Django的manage工具提供了shell命令,帮助我们配置好当前工程的运行环境(如连接好数据库等),以便可以直接在终端中执行测试python语句

    • 进入shell:python manage.py shell
  • 查看MySQL数据库日志:查看mysql数据库日志可以查看对数据库的操作记录,mysql日志文件默认没有产生

    • 打开文件,取消以下两行注释:sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf

img

    • 重启mysql服务:sudo service mysql restart
    • 打开mysql日志文件:sudo tail -f /var/log/mysql/query.log,可以实时查看数据库的日志内容

6. 数据库操作

  • 增加数据两种方法

    • save:创建模型类对象,执行对象的save()方法
1
2
3
4
5
6
7
8
>>> from book.models import BookInfo,PeopleInfo
>>> book = BookInfo(
... name='python入门',
... pub_date='2010-1-1'
... )
>>> book.save()
>>> book
<BookInfo: python入门>
    • create:通过模型类.objects.create()保存
1
2
3
4
5
>>> BookInfo.objects.create(
... name='java',
... pub_date='2021-1-1'
... )
<BookInfo: java>
  • 修改更新两种方法

    • save:查找模型类对象,修改模型类对象的属性,然后执行save()方法
1
2
3
4
5
>>> book=BookInfo.objects.get(id=2)
>>> book.readcount = 20
>>> book.save()
>>> book
<BookInfo: 射雕英雄传>
    • update:使用模型类.objects.filter().update()更新,会返回受影响的行数
1
2
>>> BookInfo.objects.filter(id=2).update(readcount=100,commentcount=200)
1
  • 删除两种方法

    • delete:查找模型类对象,然后执行delete()方法
1
2
3
>>> book=BookInfo.objects.get(id=4)
>>> book.delete()
(5, {'book.PeopleInfo': 4, 'book.BookInfo': 1})
    • delete:使用模型类.objects.filter().delete()删除,会返回受影响的行数
1
2
>>> BookInfo.objects.filter(id=1).delete()
(6, {'book.PeopleInfo': 5, 'book.BookInfo': 1})

7. 数据库查询

(1)基础条件查询

  • 基本查询

    • 模型类.objects.get():查询单一结果,返回单一对象
    • 模型类.objects.all():查询多个结果,返回列表
    • 模型类.objects.count():查询结果数量
1
2
3
4
5
6
7
8
9
# 等价于BookInfo.objects.get(id__exact=6)
>>> PeopleInfo.objects.get(id=6)
<PeopleInfo: 乔峰>

>>> PeopleInfo.objects.all()
<QuerySet [<PeopleInfo: 乔峰>, <PeopleInfo: 段誉>, <PeopleInfo: 虚竹>, <PeopleInfo: 王语嫣>, <PeopleInfo: 令狐冲>, <PeopleInfo: 任盈盈>, <PeopleInfo: 岳不群>, <PeopleInfo: 东方不败>]>

>>> PeopleInfo.objects.count()
8
  • 过滤查询:实现SQL中的where功能,表达语法:模型类.objects.过滤器(字段名__过滤条件=值)

    • filter:过滤出多个结果,返回列表(包含n个结果)
    • exclude:排除掉符合条件剩下的结果
    • get:过滤单一结果,返回1个结果
  • 过滤条件

    • 相等查询
      • exact:相等查询,区分大小写
      • iexact:相等查询,不区分大小写
    • 模糊查询
      • contains:是否包含(如果包含%无需转义),区分大小写
      • icontains:是否包含(如果包含%无需转义),不区分大小写
      • startswith:以指定值开头,区分大小写
      • istartswith:以指定值开头,不区分大小写
      • endswith:以指定值结尾,区分大小写
      • iendswith:以指定值结尾,不区分大小写
    • 空查询
      • isnull:是否为null
    • 范围查询
      • in:是否包含在范围内
    • 比较查询
      • gt:大于(greater then)
      • gte:大于等于(greater then equal)
      • lt:小于(less then)
      • lte:小于等于(less then equal)
      • 不等于的运算符,使用exclude()过滤器
    • 日期查询
      • year
      • month
      • day
      • week_day
      • hour
      • minute
      • second
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 查询书名包含'湖'的图书
BookInfo.objects.filter(name__contains='湖')
# 查询书名以'部'结尾的图书
BookInfo.objects.filter(name__endswith='部')
# 查询书名为空的图书
BookInfo.objects.filter(name__isnull=False)
# 查询编号为1或3或5的图书
BookInfo.objects.filter(id__in=[1,3,5])
# 查询编号大于3的图书
BookInfo.objects.filter(id__gt=3)
# 查询书籍id不为3的图书
BookInfo.objects.exclude(id__exact=3)
BookInfo.objects.exclude(id=3)
# 查询1980年发表的图书
BookInfo.objects.filter(pub_date__year='1986')
# 查询1990年1月1日后发表的图书
BookInfo.objects.filter(pub_date__gt='1990-1-1')

(2)F和Q对象

  • F对象:可做算数运算
  • 引入语法:from django.db.models import F
  • F对象语法:F(属性名)
1
2
3
# 查询阅读量大于等于评论量的图书。
>>> BookInfo.objects.filter(readcount__gt=F('commentcount'))
<QuerySet [<BookInfo: 雪山飞狐>]>
  • Q对象:可以使用&|~连接,&表示逻辑与,|表示逻辑或,~表示逻辑非

  • 引入语法:from django.db.models import Q

  • Q对象语法:Q(属性名__运算符=值)

1
2
3
4
5
6
7
8
# 查询阅读量大于20,或编号小于3的图书,只能使用Q对象实现
>>> BookInfo.objects.filter(Q(readcount__gt=20)|Q(id__lt=3))
<QuerySet [<BookInfo: 射雕英雄传>, <BookInfo: 天龙八部>, <BookInfo: 雪山飞狐>]>
Q对象前可以使用~操作符,表示非not。

# 查询编号不等于3的图书
>>> BookInfo.objects.filter(~Q(id=3))
<QuerySet [<BookInfo: 射雕英雄传>, <BookInfo: 天龙八部>, <BookInfo: 雪山飞狐>]>

(3)聚合函数和排序函数

  • 聚合函数:使用aggregate()过滤器调用聚合函数,返回值是一个字典类型

    • 返回字典格式:{'属性名__聚合类小写':值}
    • 聚合函数包括
      • Avg:平均
      • Count:数量
      • Max:最大
      • Min:最小
      • Sum:求和
    • 导入语法:from django.db.models import Avg,Count,Max,Min,Sum
    • 使用语法:模型类.objects.aggregate(聚合函数(字段名))

使用count时一般不使用aggregate()过滤器

1
2
3
# 查询图书的总阅读量
>>> BookInfo.objects.aggregate(Sum('readcount'))
{'readcount__sum': 126}
  • 排序:使用order_by()对结果进行排序(默认升序),语法:查询结果集.order_by()
1
2
3
4
5
6
7
# 升序
>>> BookInfo.objects.all().order_by('readcount')
<QuerySet [<BookInfo: 射雕英雄传>, <BookInfo: 笑傲江湖>, <BookInfo: 天龙八部>, <BookInfo: 雪山飞狐>]>

# 降序
>>> BookInfo.objects.all().order_by('-readcount')
<QuerySet [<BookInfo: 雪山飞狐>, <BookInfo: 天龙八部>, <BookInfo: 笑傲江湖>, <BookInfo: 射雕英雄传>]>

(4)关联查询

  • 关联查询

    • 一对多的访问语法:一对应的模型类对象.多对应的模型类名小写_set
1
2
3
>>> book = BookInfo.objects.get(id=1)
>>> book.peopleinfo_set.all()
<QuerySet [<PeopleInfo: 郭靖>, <PeopleInfo: 黄蓉>, <PeopleInfo: 黄药师>, <PeopleInfo: 欧阳锋>, <PeopleInfo: 梅超风>]>
    • 多对一的访问语法:多对应的模型类对象.多对应的模型类中的关系类属性名(外键)
1
2
3
person = PeopleInfo.objects.get(id=1)
person.book_id.name
<BookInfo: 射雕英雄传>
    • 一对多中对应一的id访问语法:多对应的模型类对象.关联类属性_id
1
2
3
>>> person = PeopleInfo.objects.get(id=1)
>>> person.book_id_id
1
  • 关联过滤查询

    • 多对一的访问语法:关联模型类名小写__属性名__条件运算符=值,如果没有”__条件运算符“部分,表示等于
1
2
3
4
5
6
7
8
9
# 查询图书,要求图书人物为"郭靖"
>>> book = BookInfo.objects.filter(peopleinfo__name='郭靖')
>>> book
<QuerySet [<BookInfo: 射雕英雄传>]>

# 查询图书,要求图书中人物的描述包含"八"
>>> book = BookInfo.objects.filter(peopleinfo__description__contains='八')
>>> book
<QuerySet [<BookInfo: 射雕英雄传>, <BookInfo: 天龙八部>]>
    • 一对多的访问语法:一模型类关联属性名__一模型类属性名__条件运算符=值,如果没有”__条件运算符“部分,表示等于
1
2
3
4
5
6
7
8
9
# 查询书名为“天龙八部”的所有人物
>>> people = PeopleInfo.objects.filter(book__name='天龙八部')
>>> people
<QuerySet [<PeopleInfo: 乔峰>, <PeopleInfo: 段誉>, <PeopleInfo: 虚竹>, <PeopleInfo: 王语嫣>]>

# 查询图书阅读量大于30的所有人物
>>> people = PeopleInfo.objects.filter(book__readcount__gt=30)
>>> people
<QuerySet [<PeopleInfo: 乔峰>, <PeopleInfo: 段誉>, <PeopleInfo: 虚竹>, <PeopleInfo: 王语嫣>, <PeopleInfo: 胡斐>, <PeopleInfo: 苗若兰>, <PeopleInfo: 程灵素>, <PeopleInfo: 袁紫衣>]>

(5)查询集QuerySet

  • 查询集:也称查询结果集、QuerySet,表示从数据库中获取的对象集合

  • 当调用如下过滤器方法时,Django会返回查询集(而不是简单的列表):

    • all():返回所有数据
    • filter():返回满足条件的数据
    • exclude():返回满足条件之外的数据
    • order_by():对结果进行排序
1
2
3
>>> books = BookInfo.objects.filter(readcount__gt=30).order_by('pub_date')
>>> books
<QuerySet [<BookInfo: 天龙八部>, <BookInfo: 雪山飞狐>]>
  • 对查询集可以再次调用过滤器进行过滤,也就意味着查询集可以含有零个、一个或多个过滤器,过滤器基于所给的参数限制查询的结果

  • 判断某一个查询集中是否有数据:查询集.exists(),如果有则返回True,没有则返回False

  • 查询集两大特性

    • 惰性执行:创建查询集不会访问数据库,直到调用数据时,才会访问数据库,调用数据的情况包括迭代、序列化、与if合用。继续执行遍历迭代操作后,才真正的进行了数据库的查询
    • 缓存:使用同一个查询集,第一次使用时会发生数据库的查询,经过存储后,Django会把结果缓存下来,再次使用这个查询集时会使用缓存的数据,减少了数据库的查询次数
  • 限制查询集:等同于sql中的limit和offset子句(不支持负数索引)

    • 对查询切片:查询集.[索引],使用索引进行切片,会返回一个新的查询集,不会立即执行查询
      • 如果获取一个对象,使用[0]等同于[0:1].get()
      • 如果没有数据,[0]会引发IndexError异常,[0:1].get()会引发DoesNotExist异常
    • 对查询集分页:Paginator(查询集,每页数据条数)
      • 获取指定页码数据:paginator.page(页数)
1
2
3
4
5
6
7
8
9
10
#查询数据
books = BookInfo.objects.all()
#导入分页类
from django.core.paginator import Paginator
#创建分页实例,每页2条
paginator=Paginator(books,2)
#获取指定页码的数据
page_skus = paginator.page(1)
#获取分页数据
total_page=paginator.num_pages

五、视图

  • 视图就是应用中views.py文件中的函数

  • 视图负责接受Web请求HttpRequest,进行逻辑处理,返回Web响应HttpResponse给请求者

    • 响应内容可以是HTML内容,404错误,重定向,json数据等
  • 视图的第一个参数必须为HttpRequest对象,还可能包含参数

    • 通过正则表达式组获取的位置参数
    • 通过正则表达式组获得的关键字参数
  • 视图必须返回一个HttpResponse对象或子对象作为响应

    • 子对象:JsonResponse
    • 子对象:HttpResponseRedirect
  • 使用视图时需要进行两步操作,两步操作不分先后

    • 配置URLconf
    • 在应用/views.py中定义视图
  • 视图处理过程

img

1. URLconf

  • 浏览者通过在浏览器的地址栏中输入网址请求网站,对于Django开发的网站,由哪一个视图进行处理请求,是由url匹配找到的
  • 请求的url被看做是一个普通的python字符串,进行匹配时不包括域名、get或post参数
  • 配置URLconf
    1. settings.py中指定url配置:ROOT_URLCONF = '项目.urls'
    2. 项目中urls.py匹配成功后,包含到应用的urls.py:path(正则, include('应用.urls'))
    3. 应用中urls.py匹配成功后,调用views.py对应的函数:path(正则, views.函数名)
  • 关于正则

    • 正则部分推荐使用 r,表示字符串不转义
    • 不能在开始加/,推荐在结束加/(虽然结尾带/能带来上述好处,但是却违背了HTTP中URL表示资源位置路径的设计理念,是否结尾带/以所属公司定义风格为准)

2. 路由命名与reverse反解析

(1)路由命名:在定义路由的时候,可以为路由命名,方便查找特定视图的具体路径信息

  • 在使用include函数定义路由时,可以使用namespace参数定义路由的命名空间

    • 语法:path(正则,include(('应用.urls',应用名),namespace=应用名))
    • 命名空间表示:凡是应用.urls中定义的路由,均属于namespace指明的应用名下
    • 命名空间的作用:避免不同应用中的路由使用了相同的名字发生冲突,使用命名空间区别开
  • 在定义普通路由时,可以使用name参数指明路由的名字,语法:path(正则,对应视图,name=路由名)

(2)reverse反解析:使用reverse函数,可以根据路由名称,返回具体的路径

  • 导入reverse:from django.urls import reverse

  • 语法

    • 对于指明命名空间的:url = reverse(命名空间namespace:路由名name)
    • 对于未指明命名空间的:url = reverse(路由名name)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('', include(('book.urls','book'),namespace='book')),
]
# book/urls.py
urlpatterns = [
path('booklist/', booklist,name='booklist'),
]
# views.py
def booklist(request):
url = reverse('book:booklist')
print(url)
return HttpResponse('index')

3. HttpRequest对象

  • 利用HTTP协议向服务器传参的几种途径

    • 提取URL的特定部分,可以在服务器端的路由中用正则表达式截取
    • 查询字符串(query string)
    • 请求体(body)中发送的数据,比如表单数据、json、xml
    • 在http报文的头中(header)

(1)URL路径参数

  • 从URL中获取值,需要在正则表达式中使用分组,获取值分为两种方式

    • 位置参数:参数的位置不能错
1
2
3
4
5
6
7
8
9
10
# book/urls.py
urlpatterns = [
re_path(r'^(\d+)/(\d+)/$', index),
]

# views.py: 参数的位置不能错
def index(request, value1, value2):
# 构造上下文
context = {'v1':value1, 'v2':value2}
return HttpResponse('index')
    • 关键字参数:参数的位置可以变,跟关键字保持一致即可
1
2
3
4
5
6
7
8
9
10
11
# book/urls.py
# 其中?P<value1>部分表示为这个参数定义的名称为value1
urlpatterns = [
re_path(r'^(?P<value1>\d+)/(?P<value2>\d+)/$', index),
]

# views.py: 参数的位置可以变,跟关键字保持一致即可
def index(request, value1, value2):
# 构造上下文
context = {'v1':value1, 'v2':value2}
return HttpResponse('index')

注意:两种参数的方式不要混合使用,在一个正则表达式中只能使用一种参数方式

(2)Django中的QueryDict对象

  • HttpRequest对象的属性GET、POST都是QueryDict类型的对象,查询字符串不区分请求方式,即假使客户端进行POST方式的请求,依然可以通过request.GET获取请求中的查询字符串数据

  • 与python字典不同,QueryDict类型的对象可以用来处理同一个键带有多个值的情况

  • 获取请求路径中的查询字符串参数(形如?k1=v1&k2=v2):request.GET,返回QueryDict对象

  • 方法get()

    • 语法:get('键',默认值)
    • 如果一个键同时拥有多个值,将获取最后一个值
    • 如果键不存在则返回None值,可以设置默认值进行后续处理
  • 方法getlist()

    • 语法:getlist('键',默认值)
    • 可以获取指定键的所有值,值以列表返回
    • 如果键不存在则返回空列表[],可以设置默认值进行后续处理
1
2
3
4
5
6
7
8
9
10
11
12
# 发送请求 127.0.0.1:8000/1/100?a=1&b=2&a=3
def index(request):
a = request.GET
print(a)
print(a.get('a'))
print(a.getlist('a'))

return HttpResponse('index')

# <QueryDict: {'a': ['1', '3'], 'b': ['2']}>
# 3
# ['1', '3']

(3)请求体

  • 请求体数据格式不固定,可以是表单类型字符串/JSON字符串/XML字符串,应区别对待

  • 请求体数据的请求方式有POST、PUT、PATCH、DELETE

  • Django默认开启了CSRF防护,会对上述请求方式进行CSRF防护验证,在测试时可以关闭CSRF防护机制

    • 方法:在settings.py文件中注释掉CSRF中间件

img

  • 表单类型 Form Data:前端发送的表单类型的请求体数据

    • 获取请求体数据:request.POST,返回QueryDict对象
1
2
3
4
5
6
7
8
9
10
11
12
13
def index(request):
a = request.POST.get('a')
b = request.POST.get('b')
alist = request.POST.getlist('a')
print(a)
print(b)
print(alist)

return HttpResponse('index')

# 3
# 2
# ['1', '3']
  • 非表单类型 Non-Form Data:非表单类型的请求体数据,Django无法自动解析

    • 获取请求体数据:request.body,返回bytes类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import json
def index(request):
# 得到json形式的字符串
a = request.body
a_str = a.decode()
print(a)
print(a_str)
# json形式的字符串转换为字典:json.loads()
# 字典转换为json形式的字符串 json.dumps()
data = json.loads(a_str)
print(data)

return HttpResponse('index')

# b'{\n "username":"itcast",\n "password":"123"\n}'
# {
# "username":"itcast",
# "password":"123"
# }
# {'username': 'itcast', 'password': '123'}

(4)请求头

  • 获取请求头headers中的数据:request.META,返回字典类型

  • 常见的请求头

    • CONTENT_LENGTH– The length of the request body (as a string)
    • CONTENT_TYPE– The MIME type of the request body
    • HTTP_ACCEPT– Acceptable content types for the response
    • HTTP_ACCEPT_ENCODING– Acceptable encodings for the response
    • HTTP_ACCEPT_LANGUAGE– Acceptable languages for the response
    • HTTP_HOST– The HTTP Host header sent by the client
    • HTTP_REFERER– The referring page, if any
    • HTTP_USER_AGENT– The client’s user-agent string
    • QUERY_STRING– The query string, as a single (unparsed) string
    • REMOTE_ADDR– The IP address of the client
    • REMOTE_HOST– The hostname of the client
    • REMOTE_USER– The user authenticated by the Web server, if any
    • REQUEST_METHOD– A string such as”GET”or”POST”
    • SERVER_NAME– The hostname of the server
    • SERVER_PORT– The port of the server (as a string)
1
2
3
4
5
6
def index(request):
a = request.META
print(a)
print(a['PATH'])

return HttpResponse(a)

(5)其他常用HttpRequest对象属性

  • method:一个字符串,表示请求使用的HTTP方法,常用值包括:’GET’、’POST’

  • user:请求的用户对象

  • path:一个字符串,表示请求页面的完整路径,不包含域名和参数部分

  • encoding:一个字符串,表示提交的数据的编码方式

    • 如果为None则表示使用浏览器的默认设置,一般为utf-8
    • 属性是可写的,可以通过修改它来修改访问表单数据使用的编码,接下来对属性的任何访问将使用新的encoding值
  • FILES:一个类似于字典的对象,包含所有的上传文件

4. HttpResponse对象

  • 视图在接收请求并处理后,必须返回HttpResponse对象或子对象
  • HttpRequest对象由Django创建,HttpResponse对象由开发人员创建

(1)HttpResponse

  • 构造响应对象:HttpResponse(content=响应体, content_type=响应体数据类型, status=状态码)
1
2
3
def index(request):
# content_type:MIME类型,语法形式:大类/小类(例:text/html)
return HttpResponse(content='index',content_type='text/html',status=200)
  • 通过HttpResponse对象属性来设置响应体、响应体数据类型、状态码

    • content:表示返回的内容,传递字符串,不要传递对象
    • status_code:返回的HTTP响应状态码,范围:100-599
1
2
3
4
def index(request):
response = HttpResponse('itcast python')
response.status_code = 400
return response
  • 设置响应头(字典形式):HttpResponse[响应头名称] = 响应头值
1
2
3
4
5
def index(request):
response = HttpResponse('itcast python', status=400)
# 自定义响应头Itcast, 值为Python
response['itcast'] = 'Python'
return response

(2)HttpResponse子类

  • Django提供了一系列HttpResponse的子类,可以快速设置状态码

    • HttpResponseRedirect:301
    • HttpResponsePermanentRedirect:302
    • HttpResponseNotModified:304
    • HttpResponseBadRequest:400
    • HttpResponseNotFound:404
    • HttpResponseForbidden:403
    • HttpResponseNotAllowed:405
    • HttpResponseGone:410
    • HttpResponseServerError:500

(3)JsonResponse

  • 若要返回json数据,可以使用JsonResponse来构造响应对象,将json数据转换为json字符串
1
2
3
4
5
from django.http import JsonResponse

def index(request):
# 设置响应头Content-Type为application/json
return JsonResponse({'city': 'beijing', 'subject': 'python'})

(4)redirect重定向

  • 跳转指定页面
1
2
3
4
from django.shortcuts import redirect

def index(request):
return redirect('/booklist')

5. 状态保持

  • 浏览器请求服务器是无状态的

    • 无状态:每次请求都是一次新的请求
    • 无状态原因:浏览器与服务器是使用Socket套接字进行通信的,服务器将请求结果返回给浏览器之后,会关闭当前的Socket连接,而且服务器也会在处理页面完毕之后销毁页面对象
  • 实现状态保持主要有两种方式

    • 在客户端存储信息使用Cookie
    • 在服务器端存储信息使用Session

(1)Cookie

  • Cookie:有时也用其复数形式Cookies,指某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)
  • Cookie由服务器端生成,发送给User-Agent(一般是浏览器),浏览器会将Cookie的key/value保存到某个目录下的文本文件内,下次请求同一网站时就发送该Cookie给服务器(前提是浏览器设置为启用cookie)
  • Cookie名称和值可以由服务器端开发自己定义,这样服务器可以知道该用户是否是合法用户以及是否需要重新登录等
  • 服务器可以利用Cookies包含信息的任意性来筛选并经常性维护这些信息,以判断在HTTP传输中的状态
  • Cookie是存储在浏览器中的一段纯文本信息,建议不要存储敏感信息如密码,因为电脑上的浏览器可能被其它人使用
  • Cookie原理
    1. 第一次请求过程
      1. 浏览器第一次请求服务器,请求头中不会携带任何cookie信息
      2. 服务器接收到请求之后,会为响应设置cookie信息(set_cookie)
      3. 浏览器接收到响应后,会将cookie信息保存起来
    1. 第二次及其之后的过程
      1. 浏览器第二次及其之后的请求,请求头中都会携带cookie信息
      2. 服务器接收到请求之后,不用再为响应设置cookie信息
  • Cookie的特点

    • Cookie以键值对的格式进行信息的存储,保存在客户端
    • Cookie基于域名,不同域名的Cookie是不能互相访问的(当浏览器请求某网站时,会将浏览器存储的跟网站相关的所有Cookie信息提交给网站服务器)
  • 设置CookieHttpResponse.set_cookie(cookie名, value=cookie值, max_age=cookie有效期)

    • max_age单位为秒,默认为None(如果是临时cookie,可将设置为None)
1
2
3
4
5
def cookie(request):
response = HttpResponse('ok')
response.set_cookie('itcast1', 'python1') # 临时cookie
response.set_cookie('itcast2', 'python2', max_age=3600) # 有效期一小时
return response
  • 读取Cookierequest.COOKIES.get(cookie名)
1
2
3
4
def cookie(request):
cookie1 = request.COOKIES.get('itcast1')
print(cookie1)
return HttpResponse('OK')
  • 删除CookieHttpResponse.delete_cookie(cookie名),或设置max_age为0

(2)Session

  • Session原理
    1. 第一次请求过程
      1. 浏览器第一次请求服务器,可以携带一些信息,请求头中没有携带任何cookie信息
      2. 服务器接收到请求之后,会为响应设置session
        1. 将session信息保存在服务器中(包含sessionid及请求携带的信息)

img

        1. 为响应设置cookie信息,key为sessionid

img

    1. 第二次及其之后的过程
      1. 浏览器第二次及其之后的请求,请求头中都会携带cookie信息(sessionid)
      2. 服务器接收到请求后,会获取session id并进行验证,验证成功后即可获取session信息

img

  • Session特点

    • 保存在服务器
    • 依赖于Cookie(若浏览器禁用了Cookie,则无法实现Session)
  • 设置Sessionrequest.session[键] = 值

1
2
3
4
5
6
def set_session(request):
print(request.COOKIES)
user_ID = 123
request.session['user_name'] = user_ID

return HttpResponse('set_session')
  • 获取Session

    • 方式一:request.session[键]
    • 方式二:request.session.get(键,默认值)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def get_session(request):
print(request.COOKIES)
# 方式一
user_id = request.session['user_name']
print(user_id)
# 方式二
user_id = request.session.get('user_name')
print(user_id)

return HttpResponse('get_session')

# {'sessionid': 'l1somhrpn6ylo0t43hwk7h6ems78vi5a'}
# 123
# 123
  • Session其他操作

    • 清除所有session,在存储中删除值部分:request.session.clear()
    • 清除所有session数据,在存储中删除键和值:request.session.flush()
    • 删除session中的指定键及值,在存储中只删除某个键及对应的值:del request.session['键']
    • 设置session的有效期:request.session.set_expiry(value)
      • 如果value为整数,session将在value秒没有活动后过期
      • 如果value为0,那么用户session的Cookie将在用户的浏览器关闭时过期
      • 如果value为None,那么session有效期将采用系统默认值, 默认为两周,可以通过在settings.py中设置SESSION_COOKIE_AGE来设置全局默认值
  • 启用Session:Django项目默认启用Session,可以在settings.py文件中查看,如需禁用session,将图中的session中间件注释掉即可

img

  • Session存储方式:在settings.py文件中,可以设置session数据的存储方式,保存在数据库、本地缓存等

    • 数据库(可以不设置,是默认存储方式)
      • 设置方式:SESSION_ENGINE='django.contrib.sessions.backends.db'
      • 如果存储在数据库中,需要在项INSTALLED_APPS中安装Session应用

img

      • 数据库中的表:django_session
      • 操作Session包括三个数据:键,值,过期时间

img

    • 本地缓存:存储在本机内存中,如果丢失则不能找回,比数据库的方式读写更快
      • 设置方式:SESSION_ENGINE='django.contrib.sessions.backends.cache'
    • 混合存储:优先从本机内存中存取,如果没有则从数据库中存取
      • 设置方式:SESSION_ENGINE='django.contrib.sessions.backends.cached_db'
    • Redis:在redis中保存session,需要引入第三方扩展,可以使用django-redis来解决
      • 安装扩展:pip install django-redis
      • 配置:在settings.py文件中做如下设置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
# 注意:若redis服务器IP不是本地回环地址,需要修改为实际ip
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}

SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'default'
class SetSession(View):
def get(self,request):
request.session['name']='123'
return HttpResponse('set session')

class GetSeesion(View):
def get(self,request):
name = request.session.get('name')
print(name)
return HttpResponse('get session')

img

6. 类视图与中间件

(1)类视图

  • 函数视图:以函数的方式定义的视图称为函数视图,函数视图便于理解;但若一个视图对应的路径中,提供了多种不同HTTP的请求方式时,则需要在一个函数中编写不同的业务逻辑,代码可读性与复用性都不佳

例:在一个注册视图中处理get和post请求
img

1
2
3
4
5
6
7
8
9
10
def register(request):
"""处理注册"""

# 获取请求方法,判断是GET/POST请求
if request.method == 'GET':
# 处理GET请求,返回注册页面
return HttpResponse('这里实现注册页面')
else:
# 处理POST请求,实现注册逻辑
return HttpResponse('这里实现注册逻辑')
  • 类视图:使用类视图可以根据视图中的不同请求方式实现类中的不同方法,定义类视图需要继承自Django提供的父类View

    • 优点:代码可读性高,相对于函数视图有更高的复用性
1
2
3
4
5
6
7
8
9
10
11
12
from django.views import View

class RegisterView(View):
"""类视图:处理注册"""

def get(self, request):
"""处理GET请求,返回注册页面"""
return HttpResponse('这里实现注册页面')

def post(self, request):
"""处理POST请求,实现注册逻辑"""
return HttpResponse('这里实现注册逻辑')
  • 类视图路由配置:使用类视图的as_view()方法来添加
1
2
3
4
urlpatterns = [
# 类视图:注册
path('1/100/',RegisterView.as_view())
]
  • 类视图原理

    • 类RegisterView继承自View类
    • RegisterView.as_view():即使用View类中的类方法
      • 初始化:as_view()方法返回view方法
      • 收到请求后便会调用view(),返回时调用dispatch()方法

img

    • dispatch()方法中会判断请求方式是否在http_method_names列表中
      • 若存在,则转化前端请求方式全部为小写,并返回执行对应方法(类RegisterView中)
      • 若不存在,则返回错误状态码(默认405)

img

img

  • 类视图的多继承重写:使用面向对象多继承的特性(遵循MRO的继承顺序)

例:判断是否为登录状态,若登录则显示,若未登录则不显示

1
2
3
4
5
6
7
8
from django.contrib.auth.mixins import LoginRequiredMixin

class CenterView(LoginRequiredMixin,View):
def get(self,request):
return HttpResponse("OK")

def post(self,request):
return HttpResponse("OK")
  • 若未登录,则跳转页面

img

(2)中间件

  • Django中的中间件是一个轻量级、底层的插件系统,可以介入Django的请求和响应处理过程,修改Django的输入或输出

  • 中间件的设计为开发者提供了一种无侵入式的开发方式,增强了Django框架的健壮性

  • 定义中间件

    • 定义一个中间件工厂函数,然后返回一个可以被调用的中间件
    • 中间件工厂函数需要接收一个可以调用的get_response对象
    • 返回的中间件也是一个可以被调用的对象,并且像视图一样需要接收一个request对象参数,返回一个response对象
1
2
3
4
5
6
7
8
9
10
def simple_middleware(get_response):
# 此处编写的代码仅在Django第一次配置和初始化的时候执行一次

def middleware(request):
# 此处编写的代码会在每个请求处理视图前被调用
response = get_response(request)

return response

return middleware

img

  • 注册中间件:定义好中间件后,需要在settings.py文件中添加注册中间件

img

  • 中间件初始化(Django运行在调试模式下,中间件初始化部分内容有可能被调用两次)

img

  • 定义视图并调用

img

img

  • 多个中间件的执行顺序(类似递归)

    • 在请求视图被处理前,中间件由上至下依次执行
    • 在请求视图被处理后,中间件由下至上依次执行

img

img

img

六、模版

1. 自带模版

(1)定义与调用

  • 设置模版:在工程中创建模板目录templates,在settings.py配置文件中修改TEMPLATES配置项的DIRS值

img

  • 定义模板:在templates目录中新建一个模板文件
1
2
3
4
5
6
7
8
9
10
11
12
13
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div>{{ city }}</div>
</body>
</html>
  • 调用模板

    • 导入模块:from django.template import loader
    • 找到模板,返回模板对象:loader.get_template(模板文件路径)
    • 渲染模板,返回渲染后的html文本字符串 :模板对象.render(context=None, request=None)
      • context:模板变量字典,默认值为None
      • request:请求对象,默认值为None
1
2
3
4
5
from django.template import loader
def index(request):
template = loader.get_template('../templates/index.html')
context = {'city':'杭州'}
return HttpResponse(template.render(context))
  • Django提供了函数render简写方式:render(request对象, 模板文件路径, 模板数据字典)
1
2
3
4
from django.shortcuts import render
def index(request):
context = {'city':'武汉'}
return render(request,'../templates/index.html',context)

(2)语法

  • 模板变量(模板变量可以使python的内建类型,也可以是对象)

    • 变量名必须由字母、数字、下划线(不能以下划线开头)和点组成
    • 变量语法:{{变量}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def index(request):
context = {
'city': '北京',
'adict': {
'name': '西游记',
'author': '吴承恩'
},
'alist': [1, 2, 3, 4, 5]
}
return render(request,'../templates/index.html',context)
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div>{{ city }}</div>
<div>{{ adict }}</div>
<div>{{ adict.name }}</div>
<div>{{ alist }}</div>
<div>{{ alist.0 }}</div>
</body>
</html>
  • 模版语句

    • for循环
      • 开始:{% for item in 列表 %}` - 循环逻辑 - - - - `{{ forloop }}`:有不同选项表示循环顺序 - `{% empty %}`:列表为空或不存在时执行此逻辑 - - - 结束:`{% endfor %}
1
2
3
4
5
6
7
<div>
{% for item in adict %}
<li>{{forloop}}</li>
<!-- {'parentloop': {}, 'counter0': 0, 'counter': 1, 'revcounter': 2, 'revcounter0': 1, 'first': True, 'last': False} -->
<li>{{forloop.counter}} {{item}}</li>
{% endfor %}
</div>
    • if条件
      • 开始:{% if 条件语句 %}` - 条件逻辑 - - - - 逻辑1:`{% elif 条件语句 %}` - 逻辑2:`{% else 条件语句 %}` - - - 结束:`{% endif %}

运算符左右两侧不能紧挨变量或常量,必须有空格

比较运算符 ==``!= < > <= >=
布尔运算符 and or not
1
2
3
4
5
6
7
<div>
{% if city == '北京' %}
<li>我们生活在北京</li>
{% else %}
<li>我们没有生活在北京</li>
{% endif %}
</div>
    • 注释
      • 单行注释语法:``
      • 多行注释语法:
        • 开头:{% comment %}` - 结束:`{% endcomment %}

(3)过滤器

  • 过滤器:使用管道符号|来应用过滤器,用于进行计算、转换操作,可以使用在变量、标签中;如果过滤器需要参数,则使用:传递参数,语法:变量|过滤器:参数

  • 过滤器:

    • safe:禁用转义,告诉模板变量是安全的,可以解释执行
    • length:长度,返回字符串包含字符的个数,或列表、元组、字典的元素个数
    • default:默认值,如果变量不存在时则返回默认值,语法:变量|default:'默认值'
    • date:日期,用于对日期类型的值进行字符串格式化,语法:变量|date:'格式化样式'
      • Y:表示年,格式为4位,如2022
      • y:表示年,格式为2位,如22
      • m:表示月,格式为数字,如01
      • M:表示月,格式为汉字,如十月
      • d:表示日, 格式为数字,如01
      • D:表示日, 格式为星期,如星期一
      • j:表示日,格式为数字,如1
      • H:表示时,24进制
      • h:表示时,12进制
      • i:表示分,为0-59
      • s:表示秒,为0-59

(4)模版继承

  • 模板继承和类的继承含义是一样的,主要是为了提高代码重用,减轻开发人员的工作量

  • 父模板:如果发现在多个模板中某些内容相同,那就应该把这段内容定义到父模板中

    • 标签block:用于在父模板中预留区域,留给子模板填充差异性的内容;为了更好的可读性,建议给标签写上名字
      • 开始:{% block 名字%}` - 结束:`{% endblock 名字%}
    • 父模板中也可以使用上下文中传递过来的数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>{% block title %}{% endblock title%}</title>
</head>
<body>
{% block main%}
<p>主要内容</p>
{% endblock main%}

{% block footer%}
<p>脚注部分</p>
{% endblock footer%}

</body>
</html>
  • 子模版:子模版不用填充父模版中的所有预留区域,如果子模版没有填充,则使用父模版定义的默认值

    • 标签extends:继承,写在子模板文件的第一行,语法:{% extends "父模版路径" %}
    • 填充父模板中指定名称的预留区域,直接用指定名字的block标签进行修改即可
    • 获取父模版中block的内容:{{ block.super }}
1
2
3
4
5
6
{% extends "fa_index.html" %}

{% block main %}
这里是子模版内容
{{ block.super }}
{% endblock main %}

2. Jinja2模版

  • Jinja2:是Python下一个被广泛应用的模板引擎,是由Python实现的模板语言,他的设计思想来源于 Django 的模板引擎,并扩展了其语法和一系列强大的功能,尤其是Flask框架内置的模板语言
  • 由于django默认模板引擎功能不齐全,速度慢,所以可以在Django中使用jinja2,jinja2宣称比django默认模板引擎快10-20倍
  • Django主流的第三方APP基本上也都同时支持Django默认模板及jinja2,所以要用jinja2也不会有多少障碍

(1)安装与设置

  • 安装jinja2模块:pip install jinja2
  • 在项目文件中创建一个jinja2.py文件,文件中引用Environment
1
2
3
4
5
from jinja2 import Environment

def environment(**options):
env = Environment(**options)
return env
  • 修改settings文件

    • TEMPLATES变量修改:'BACKEND': 'django.template.backends.jinja2.Jinja2'
    • OPTIONS键中添加:'environment':'jinja2文件名.environment'

注意:需要保留原来的django设置,并放在jinja2设置后面,否则会报错

img

(2)语法

  • jinja2模板语法绝大多数和Django自带模板一样
  • jinja2中:forloop为loop
变量 赋值
loop.index 当前循环迭代的次数(从1开始)
loop.index0 当前循环迭代的次数(从0开始)
loop.revindex 当循环结束需要迭代的次数(从1开始)
loop.revindex0 当循环结束需要迭代的次数(从0开始)
loop.first 如果是第一次迭代,为True
loop.last 如果是最后一次迭代,为True
loop.length 序列中的项目数
loop.cycle 在一串序列间期取值的辅助函数

(3)自定义过滤器

  • 在views.py的同级文件夹新建一个python包(包含__init__.py文件):templatetags
  • templatetags包下新建一个自定义过滤器文件,并自定义过滤器
1
2
3
4
5
6
7
8
from django import template

# template.Library实例
register = template.Library()
# 注册过滤器
@register.filter(name='do')
def do(a):
return a+200
  • 在settings.py中注册自定义过滤器:'应用名.templatetags'
  • 在模版文件中使用过滤器,文件最前面需要加上:{% load 自定义过滤器文件名 %}
1
2
3
{% load custom_filter %}

{{abc|do}}

3. CSRF

  • CSRF全拼为Cross Site Request Forgery,译为跨站请求伪造,容易威胁个人隐私以及财产安全
  • CSRF攻击示意图(客户端访问服务器时没有同服务器做安全验证)

img

  • 防止 CSRF 攻击

原理:浏览器的同源策略,网站B获取不到网站A的 cookie ,所以可以解决跨站请求伪造的问题

    • 在客户端向后端请求界面数据的时候,后端会往响应中的 cookie 中设置 csrf_token 的值
    • 在 Form 表单中添加一个隐藏的的字段,值也是 csrf_token
    • 在用户点击提交的时候,会带上这两个值向后台发起请求
    • 后端接受到请求,会做以下两件事:
      • 从cookie中取出 csrf_token 的值
      • 从表单数据中取出来隐藏的 csrf_token 的值
    • 如果比较之后两值一样,那么代表是正常的请求;如果没取到或者比较不一样,代表不是正常的请求,不执行下一步操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 导入生成 csrf_token 的函数
from django.middleware.csrf import get_token
csrf_token = get_token(request)

def get(self, request):
# 生成csrf_token
from django.middleware.csrf import get_token
csrf_token = get_token(request)

# 渲染转换页面,传入 csrf_token 到模板中
response = render(request, 'transfer.html',context={'csrf_token':csrf_token})

# 设置csrf_token到cookie中,用于提交校验
response.set_cookie('csrf_token', csrf_token)

return response
<head>
<meta charset="UTF-8">
<title>转账</title>
</head>
<body>
<h1>我是网站A,转账页面</h1>

<form method="post">
<!-- 在转账模板表单中添加 csrf_token 隐藏字段 -->
<input type="hidden" name="csrftoken" value="{{ csrf_token }}">
<label>账户:</label><input type="text" name="to_account" placeholder="请输入对方账户"><br/>
<label>金额:</label><input type="number" name="money" placeholder="请输入转账金额"><br/>
<input type="submit" value="转账">
</form>

</body>
</html>
# 取出表单中的 csrf_token
form_csrf_token = request.POST.get("csrftoken")
# 取出 cookie 中的 csrf_token
cookie_csrf_token = request.COOKIES.get('csrf_token')
# 进行对比
if cookie_csrf_token != form_csrf_token:
return HttpResponse('token校验失败,可能是非法操作')
  • 在 Django项目中解决 CSRF 攻击

    • Django中开启CSRF(默认开启)

img

    • 模板中设置 CSRF 令牌
1
2
3
{% csrf_token %}
# 或者
<input type="hidden" value="{{ csrf_token }}">

附录-参考资料

官方网站

官方网站

Github源码

Github源码

4.1版中/英文文档

https://docs.djangoproject.com/zh-hans/4.1/

https://docs.djangoproject.com/en/4.1/

Django Book教程

Django Book 教程

Tange With Django教程

Tange With Django 教程