深色模式
Django 进阶 学习笔记
Django 官网中文文档 —— https://docs.djangoproject.com/zh-hans/
Django 使用 MySQL 数据库
版本支持
Django 支持 MySQL 5.7 及以上版本。
Django 的 inspectdb
功能使用 information_schema
数据库,其中包含所有数据库架构的详细数据。
Django 希望数据库支持 Unicode
(UTF-8 编码),并将执行事务和引用完整性的任务交给它。
存储引擎
MySQL 的默认存储引擎是 InnoDB
。这个引擎是完全事务性的,并且支持外键引用。这是推荐的选择。然而,InnoDB
自动增量计数器在 MySQL 重启时丢失,因为它不记得 AUTO_INCREMENT
值,而是将其重新创建为 "max(id)+1"
。这可能会导致无意中重用 AutoField
值。
MyISAM 的主要缺点是不支持事务,也不执行外键约束。
MySQL 数据库 API 驱动程序
mysqlclient 是一个原生驱动。它是推荐的选择。Django 需要 mysqlclient 1.4.0 或更高版本。
MySQL Connector/Python 是一个来自 Oracle 的纯 Python 驱动,不需要 MySQL 客户端库或标准库之外的任何 Python 模块。
除了数据库 API 驱动之外,Django 还需要一个适配器来从其 ORM 中访问数据库驱动。Django 为 mysqlclient 提供了一个适配器,而 MySQL Connector/Python 则包含了 自己的 。但它可能不支持最新版本的 Django。
在 WSL 安装 MySQL
安装 mysqlclient
使用 MySQL 数据库需要安装驱动程序:
sh
# Debian / Ubuntu
sudo apt-get install python3-dev default-libmysqlclient-dev build-essential
# Red Hat / CentOS
sudo yum install python3-devel mysql-devel
# pip 方式安装
pip install mysqlclient
1
2
3
4
5
6
2
3
4
5
6
修改 Django 设置 连接数据库
打开项目 settings.py
修改 DATABASES
配置信息
py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mysite', # 数据库名字
'USER': 'dbuser', # 数据库用户名
'PASSWORD': '123456', # 数据库用户密码
'HOST': 'localhost', # 数据库主机
'PORT': '3306', # 数据库端口
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
Bash
python3 manage.py makemigrations # 生成迁移
python3 manage.py migrate # 应用迁移
1
2
2
数据库 API
shell 工具
Django 的 manage 工具提供了 shell 命令,帮助我们配置好当前工程的运行环境(如连接好数据库等),以便可以直接在终端中执行测试 python 语句
sh
python manage.py shell
1
导入模型类
sh
from book.models import BookInfo,PeopleInfo
1
数据库操作
增加方法1:save
通过创建模型类对象,执行对象的 save()
方法保存到数据库中
增加方法2:create
通过模型类 .objects.create()
保存
修改方法1:save
修改模型类对象的属性,然后执行 save()
方法
修改方法2:update
使用模型类 .objects.filter().update()
,会返回受影响的行数
删除方法1:模型类对象 delete
删除方法2:模型类 .objects.filter().delete()
基本查询
get
查询单一结果,如果不存在会抛出模型类 .DoesNotExist
异常
sh
BookInfo.objects.get(id=1)
1
all
查询多个结果
sh
BookInfo.objects.all()
1
count
查询结果数量
sh
BookInfo.objects.count()
1
过滤查询
filter
过滤出多个结果exclude
排除掉符合条件剩下的结果get
过滤单一结果
过滤条件的语法:属性名称__比较运算符=值
# 属性名称和比较运算符间使用两个下划线,所以属性名不能包括多个下划线
相等 exact:
表示判等
sh
BookInfo.objects.filter(id__exact=1)
1
模糊查询
比较查询 不等于的运算符,使用 exclude()
过滤器
Bash
BookInfo.objects.exclude(id=3)
1
contains
:是否包含
sh
BookInfo.objects.filter(name__contains='传')
1
startswith
、endswith
:以指定值开头或结尾
sh
BookInfo.objects.filter(name__endswith='部')
1
空查询 isnull
:是否为 null
sh
BookInfo.objects.filter(name__isnull=True)
1
范围查询 in
:是否包含在范围内
sh
BookInfo.objects.filter(id__in=[1,3,5])
1
比较查询: gt
大于,gte
大于等于,lt
小于,lte
小于等于
sh
BookInfo.objects.filter(id__gt=3)
1
日期查询 year
、month
、day
、week_day
、hour
、minute
、second
sh
BookInfo.objects.filter(pub_date__year=1980)`
1
F 和 Q 对象
F对象:属性比较,语法 F(属性名)
例:查询阅读量大于2倍评论量的图书
py
from django.db.models import F
BookInfo.objects.filter(readcount__gt=F('commentcount')*2)
1
2
2
Q对象:多个过滤器逐个调用表示逻辑与关系,同 sql
语句中 where
部分的 and
关键字
例:查询阅读量大于20,并且编号小于3的图书
py
from django.db.models import F
BookInfo.objects.filter(readcount__gt=20,id__lt=3)
1
2
2
py
BookInfo.objects.filter(readcount__gt=20).filter(id__lt=3)
1
Q 对象可以使用 &、|
连接,&
表示逻辑与,|
表示逻辑或
例:查询阅读量大于20,或编号小于3的图书,只能使用 Q 对象实现
py
BookInfo.objects.filter(Q(readcount__gt=20)|Q(id__lt=3))
1
Q 对象前可以使用 ~
操作符,表示非 not
例:查询编号不等于3的图书
py
BookInfo.objects.filter(~Q(id=3))
1
聚合函数和排序函数
聚合函数,使用 aggregate()
过滤器,聚合函数包括:Avg
平均,Count
数量,Max
最大,Min
最小,Sum
求和,被定义在 django.db.models
中
例:查询图书的总阅读量
sh
from django.db.models import Sum
BookInfo.objects.aggregate(Sum('readcount'))
1
2
2
排序,使用 order_by
对结果进行排序
例:默认升序 和 降序
sh
BookInfo.objects.all().order_by('readcount')
1
sh
BookInfo.objects.all().order_by('-readcount')
1
关联查询
一到多 例:查询书籍为1的所有人物信息
sh
book = BookInfo.objects.get(id=1)
book.peopleinfo_set.all()
1
2
2
多到一 例:查询人物为1的书籍信息
sh
person = PeopleInfo.objects.get(id=1)
person.book
1
2
2
访问一对应的模型类关联对象的 id
例:查询人物为1的书籍编号
sh
person = PeopleInfo.objects.get(id=1)
person.book_id
1
2
2
关联过滤查询
由多模型类条件查询一模型类数据
例:查询图书,要求图书人物为"郭靖"
sh
book = BookInfo.objects.filter(peopleinfo__name='郭靖')
book
1
2
2
例:查询图书,要求图书中人物的描述包含"八"
sh
book = BookInfo.objects.filter(peopleinfo__description__contains='八')
book
1
2
2
由一模型类条件查询多模型类数据
例:查询书名为“天龙八部”的所有人物
sh
people = PeopleInfo.objects.filter(book__name='天龙八部')
people
1
2
2
例:查询图书阅读量大于30的所有人物
sh
people = PeopleInfo.objects.filter(book__readcount__gt=30)
people
1
2
2
查询集 QuerySet
当调用过滤器方法时,Django 会返回查询集 QuerySet
,表示从数据库中获取的对象集合all()
:返回所有数据filter()
:返回满足条件的数据exclude()
:返回满足条件之外的数据order_by()
:对结果进行排序,查询集可以含有零个、一个或多个过滤器。exists()
:判断查询集中是否有数据,如果有则返回 True
,没有则返回 False
查询集特性1 惰性执行:
创建查询集不会访问数据库,直到调用数据时,才会访问数据库,调用数据的情况包括迭代、序列化、与 if
合用
例:当执行如下语句时,并未进行数据库查询,只是创建了一个查询集
sh
books
books = BookInfo.objects.all()
1
2
2
继续执行遍历迭代操作后,才真正的进行了数据库的查询
sh
for book in books:
print(book.name)
1
2
2
查询集特性2 缓存:
使用同一个查询集,第一次使用时会发生数据库的查询,然后 Django 会把结果缓存下来,再次使用这个查询集时会使用缓存的数据,减少了数据库的查询次数 情况一:如下是两个查询集,无法重用缓存,每次查询都会与数据库进行一次交互,增加了数据库的负载
sh
[book.id for book in BookInfo.objects.all()]
[book.id for book in BookInfo.objects.all()]
1
2
2
情况二:经过存储后,可以重用查询集,第二次使用缓存中的数据
sh
books=BookInfo.objects.all()
[book.id for book in books]
[book.id for book in books]
1
2
3
2
3
限制查询集:可以对查询集进行取下标或切片操作,等同于 sql
中的 limit
和 offset
子句,注意:不支持负数索引
对查询集进行切片后返回一个新的查询集,不会立即执行查询。
如果获取一个对象,直接使用[0]
,等同于[0:1].get()
,但是如果没有数据,[0]
引发IndexError
异常,[0:1].get()
如果没有数据引发DoesNotExist
异常。
例:获取第1、2项,运行查看
sh
books = BookInfo.objects.all()[0:2]
books
1
2
2
分页 查看官方文档 【分页】
sh
#查询数据
books = BookInfo.objects.all()
#导入分页类
from django.core.paginator import Paginator
#创建分页实例
paginator=Paginator(books,2)
#获取指定页码的数据
page_skus = paginator.page(1)
#获取分页数据
page_skus.object_list
#获取总页数
total_page=paginator.num_pages
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
使用 AJAX,CSRF 和 CORS
Javascript 客户端
如果你正在构建一个 JavaScript 客户端来和 Web API 对接,你将需要考虑客户端是否可以使用网站其他部分使用的身份验证策略,并确定是否需要使用 CSRF 令牌或 CORS 标头。
与正在交互的 API 相同的上下文中发起的 AJAX 请求通常将使用 SessionAuthentication
。 这确保一旦用户登录,任何 AJAX 请求都可以使用与网站其他部分相同的基于会话的身份验证进行身份验证。
与其通信的 API 在不同站点上发起的 AJAX 请求通常需要使用基于非会话的身份验证方案,例如TokenAuthentication
。
CSRF
CSRF (Cross-Site Request Forgery) 是一种攻击方式,攻击者通过伪造用户的请求,使得用户在不知情的情况下执行了恶意操作。当用户没有注销登出网站并且具有有效的会话时,这种攻击可能发生。在这种情况下,恶意站点可能会在登录会话的上下文中针对目标站点执行攻击操作。
CSRF 防范方法:使用随机令牌验证请求来源、检查 Referer 头部、限制 HTTP 方法、验证码等方式来验证请求的合法性。
为了防范这些类型的攻击,你需要做两件事情:
- 确保“安全”的 HTTP 操作(如:GET,HEAD 和 OPTIONS)不能用于更改任何服务器端状态。
- 确保“不安全”的 HTTP 操作(如:POST,PUT,PATCH 和 DELETE)始终需要有效的 CSRF 令牌。
如果你使用的是 SessionAuthentication
则需要为任何 POST,PUT,PATCH 或 DELETE 操作包含有效的 CSRF 令牌。
为了使用 AJAX 请求,你需要在 HTTP 标头中包含 CSRF 令牌
方法一:先通过 get 方法获取 csrf_token 的值,然后缓存起来。之后每次发起 post 异步请求时,将获取到的 csrf_token 值携带上。
在 django 中创建一个获取 csrf_token 的视图函数:
py
from django.middleware.csrf import get_token
def get_csrf_token(request):
print(request)
csrf_token = get_token(request) # 获取csrf_token的值
print(csrf_token)
return json_response(data={'token': csrf_token})
1
2
3
4
5
6
2
3
4
5
6
前端在 POST 请求之前先通过 get 的方式把 csrf_token 获取到,并缓存起来
py
import axios from 'axios'
// 获取csrf_token
axios.get('/api/home/token/').then(res => {
let csrf_token = res.data.data.token
window.sessionStorage.setItem("csrf_token", csrf_token)
})
// post请求,配置请求头
axios({
url: '/api/auth/login/',
method: 'post',
data: {
username: this.username,
password: this.password
},
headers: {
'Content-Type':'application/x-www-form-urlencoded',
'X-CSRFToken': window.sessionStorage.getItem("csrf_token")
}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
方法二:前端设置 axios 请求拦截器,从 cookie 中获取 csrftoken 的值,然后在请求头中添加 X-CSRFToken等配置项。以解决后端接口 csrf 验证问题。
py
import axios from 'axios'
// 创建一个axios实例
const axios_instance = axios.create()
// 设置axios拦截器:请求拦截器
axios_instance.interceptors.request.use(config => {
//请求拦截一般会有哪些操作
// 1.比如config中的一些信息不符合服务器的要求,这里可以做一些修改
// 2.比如每次发送网络请求时,都希望在界面中显示一个请求的图标(然后再响应拦截中取消显示)
// 3.某些网络请求必须携带一些特殊的信息(如登录token),如果没有携带就可以拦截并作响应提示
// 给请求头添加token
/*
* 其中 /.*csrftoken=([^;.]*).*$/ 是一个正则表达式,用于从cookie中获取csrftoken的值 ,
* ([^;.]*) 是命名捕获,表示从匹配到的内容中 只获得 ()内的值。
* string.match(regex) 得到的是一个数组, 第0项是匹配到的全部内容,第1项是通过命名捕获得到的内容,在这里就是csrftoken的值。
* 这样就完成了使用axios发送请求的正确配置了,同时保证了网站免受csrf攻击的影响.
*/
config.headers['X-Requested-With'] = 'XMLHttpRequest';
let regex = /.*csrftoken=([^;.]*).*$/; // 用于从cookie中匹配 csrftoken值
config.headers['X-CSRFToken'] = document.cookie.match(regex) === null ? null : document.cookie.match(regex)[1];
return config
}, err => {
// 请求未成功发出,如:没有网络...
return Promise.reject(err)
})
/*
// 设置axios拦截器: 响应拦截器
axios_instance.interceptors.response.use(res => {
// 成功响应的拦截
return Promise.resolve(res.data)
}, err =>{
// 失败响应的拦截
console.log(err)
if(err.response){
// 失败响应的status需要在response中获得
console.log(err.response)
switch(err.response.status){
// 对得到的状态码的处理,具体的设置视自己的情况而定
case 401:
console.log('未登录')
window.location.href='/'
break
case 404:
window.location.href='/'
break
case 405:
console.log('不支持的方法')
break
// case ...
default:
console.log('其他错误')
break
}
}
// 注意这里应该return promise.reject(),
// 因为如果直接return err则在调用此实例时,响应失败了也会进入then(res=>{})而不是reject或catch方法
return Promise.reject(err)
})
*/
export {
axios_instance
}
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
CORS
CORS (Cross-Origin Resource Sharing) 是一种浏览器安全机制,用于控制在不同源(域、协议或端口)之间共享资源的方式。它通过HTTP头部信息进行通信,允许服务器指定哪些源可以访问其资源,并且限制了客户端可以发送的请求类型。这有助于防止跨站点脚本攻击(XSS)和跨站点请求伪造(CSRF)等安全问题。
前端可通过代理访问后端解决跨域
编辑 vue.config.js
js
module.exports = {
devServer: {
proxy: {
'/api': {
target: `http://127.0.0.1:8000/api`,
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
后端可通过安装 django 中间件来解决跨域方式
django-cors-headers 是一个 Django 应用程序,用于处理跨源资源共享(CORS)请求。它通过添加必要的 HTTP 响应头来允许从其他域名或端口访问 Django 应用程序的资源。这对于使用 JavaScript 进行 AJAX 请求的 Web 应用程序非常有用。
安装 django-cors-headers
sh
python -m pip install django-cors-headers
1
python -m pip install 和 pip install 有何区别
在大多数情况下,python -m pip install 和 pip install 是等效的。它们都用于安装 Python 包。但是,使用 python -m pip install 可以确保您使用的是与当前正在运行的 Python 解释器版本相对应的 pip。这对于在系统上同时安装了多个 Python 版本时特别有用。另外,如果您使用虚拟环境,则必须使用 python -m pip install 来确保包被正确地安装到虚拟环境中。
配置 settings.py
文件
py
INSTALLED_APPS = [
# ...
'corsheaders', # 启用 django-cors-headers
# ...
]
MIDDLEWARE = [
# ...
# django-cors-headers 您还需要添加一个中间件类来监听响应:
# 注意 CorsMiddleware 当前位置,应放在任何可以生成响应的中间件之前。
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
# ...
]
# django-cors-headers 被授权进行跨站点 HTTP 请求的来源列表。
CORS_ALLOWED_ORIGINS = [
"http://localhost:8080",
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19