Django¶
Django模型¶
字段¶
字段名 | verbose_name(字段显示名称) | 取值范围 |
---|---|---|
big_auto_field | 大自增 ID | |
char_field | 字符串字段 | |
text_field | 文本字段 | |
boolean_field | 布尔字段 | |
email_field | 邮箱字段 | |
image_field | 图片字段 | |
json_field | JSON 字段 | |
url_field | URL 字段 | |
uuid_field | UUID 字段 | |
slug_field | Slug 字段 | |
binary_field | 二进制字段 | |
file_field | 文件字段 | |
file_path_field | 文件路径字段 | |
generic_ip_address_field | IP 地址字段 | |
time_field | 时间字段 | |
date_field | 日期字段 | |
datetime_field | 日期时间字段 | |
duration_field | 时间间隔字段 | |
decimal_field | 小数字段 | |
float_field | 浮点数字段 | |
small_integer_field | 小整数字段 | (-32768 到 32767) |
integer_field | 整数字段 | (-2147483648 到 2147483647) |
positive_small_integer_field | 正小整数字段 | (0 到 32767) |
positive_integer_field | 正整数字段 | (0 到 2147483647) |
big_integer_field | 大整数字段 | (0到9223372036854775807) |
positive_big_integer_field | 正大整数字段 | (0到18446744073709551615) |
字段选项¶
以下是整合了 字段选项作用、前后端分离场景无用原因,并补充 补充说明 / 其他场景价值 的完整表格,更清晰梳理每个选项的特点:
字段选项 | 作用详细说明 | 前后端分离中 “无用” 的原因 | 补充说明 / 其他场景价值 |
---|---|---|---|
null |
控制数据库层面字段是否允许 NULL 值(数据库约束) |
前后端分离不直接影响,实际仍需关注(因涉及数据库存储) | 与 blank 配合处理空值逻辑,数值、日期类型字段常用 null=True ,字符串字段建议用 blank=True 避免数据库存 NULL |
blank |
控制表单验证(如 Admin、ModelForm)时是否允许空值(表单逻辑) | 前后端分离后,前端表单验证独立实现,无需依赖 Django 内置 blank 逻辑 |
纯后端逻辑(如数据导入、脚本生成数据)仍需用 blank 控制模型层空值规则 |
choices |
提供固定选项列表(如 [(1, "是"), (2, "否")] ),约束字段取值并优化 Admin 展示 |
前端自定义下拉 / 单选组件,不依赖 Django choices ;但后端数据校验仍可复用 choices 值 |
需保证前后端选项同步,可通过接口返回 choices 列表给前端,或维护独立枚举文件(如 Python 枚举类) |
db_column |
自定义数据库字段名(默认用模型字段名) | 前后端分离不影响,实际仍需设置(数据库命名规范需求) | 对接旧数据库、遵循特定命名规范(如全小写、下划线分隔)时必用 |
db_comment |
给数据库字段加注释(Django 3.2+ 支持) | 前后端分离不影响,实际仍需设置(数据库运维、协作需求) | 大型项目 / 需对接 DBA 时,补充字段业务含义(如 db_comment="存储用户真实姓名" ) |
db_default |
数据库层面设置默认值(与模型 default 功能重叠,但作用于数据库) |
前后端分离不影响,实际少用(模型 default 更便捷) |
复杂默认值需直接写入数据库时使用(如数据库函数 NOW() ),但多数场景用模型 default 更简单 |
db_index |
数据库层面创建索引,加速查询 | 前后端分离不影响,实际仍需设置(性能优化需求) | 频繁作为查询条件的字段(如手机号、订单号)必用,需平衡索引对写性能的损耗 |
db_tablespace |
指定索引所在表空间(需数据库支持,如 PostgreSQL ) | 前后端分离不影响,实际少用(需复杂数据库分区管理) | 数据库分表空间优化时使用,小型项目基本用不到 |
default |
模型层面设置默认值(创建实例时自动填充) | 前后端分离不影响,实际仍需设置(数据初始化需求) | 通用默认值(如时间 default=timezone.now 、状态 default="待处理" )必用 |
editable |
控制字段在 Admin/ModelForm 中是否可编辑 | 前后端分离后,前端表单独立实现,无需依赖 Django editable 控制 |
纯后端逻辑(如自动生成字段)可设 editable=False 避免误操作 |
error_messages |
自定义验证失败的错误提示(如 {"max_length": "长度超限"} ) |
前后端分离后,前端需独立实现错误提示,后端 error_messages 仅作用于 Django 内置验证逻辑 |
若用 Django REST Framework 等框架,error_messages 可通过接口返回给前端,仍有复用价值 |
help_text |
在 Admin/ModelForm 中显示字段辅助说明 | 前后端分离后,前端需独立实现提示文案,无需依赖 Django help_text |
纯后端管理(如 Admin )场景仍需用,可通过接口返回 help_text 给前端复用 |
primary_key |
设置字段为主键(替代默认 id 主键) |
前后端分离不影响,实际仍需设置(主键设计需求) | 用 UUID、业务编号(如订单号)做主键时必用,需注意主键字段不可变、非空等特性 |
unique |
数据库层面约束字段值唯一 | 前后端分离不影响,实际仍需设置(数据唯一性需求) | 用户名、邮箱等唯一标识字段必用,需配合前端校验避免重复提交 |
unique_for_date |
约束 “日期 + 字段” 组合在同一天内唯一(如同一日期的标题不重复) | 前后端分离后,前端无法直接复用该逻辑,需独立实现日期 + 字段的唯一性校验 | 特定业务场景(如日报标题按天唯一)仍可用,但需保证前后端校验逻辑同步(如前端拦截 + 后端 unique_for_date ) |
unique_for_month |
约束 “月份 + 字段” 组合在同一月内唯一 | 前后端分离后,前端无法直接复用该逻辑,需独立实现月份 + 字段的唯一性校验 | 特定业务场景(如月报标题按月唯一)仍可用,需保证前后端校验同步 |
unique_for_year |
约束 “年份 + 字段” 组合在同一年内唯一 | 前后端分离后,前端无法直接复用该逻辑,需独立实现年份 + 字段的唯一性校验 | 特定业务场景(如年报标题按年唯一)仍可用,需保证前后端校验同步 |
verbose_name |
设置字段友好名称(如 verbose_name="用户姓名" ) |
前后端分离后,前端界面名称独立设计,无需依赖 Django verbose_name |
纯后端管理(如 Admin )场景仍需用,可通过接口返回 verbose_name 给前端复用 |
validators |
自定义验证逻辑(如正则、范围校验) | 前后端分离后,前端需独立实现验证逻辑,后端 validators 仅作用于 Django 内置流程 |
若用 Django REST Framework ,validators 可通过接口返回错误给前端,仍有复用价值 |
mysql¶
# ubuntu22.04 安装 mysql
sudo apt install mysql- server
# 修改root 用户密码
ALTER USER 'root'@'localhost' IDENTIFIED BY 'mysql';
sudo apt-get install python3-dev default -libmysqlclient-dev build-essential
pip install mysqlclient
# 创建数据库
create database bankaccount charset = utf8;
# 创建用户
CREATE USER 'custom'@'%' IDENTIFIED BY 'mysql';
# 给用户权限
GRANT ALL ON bankaccount.* TO 'custom'@'%';
# 刷新
flush privileges;
# 测试连接
mysql -ucustom -pmysql
# 删除用户
DROP USER 'custom'@'%';
# Settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'bankaccount',
'HOST': '127.0.0.1',
'POST': '3306',
'USER': 'custom',
'PASSWORD': 'mysql',
}
}
数据查询¶
all()¶
# 1.全表查询
select * from users_blog
>>> b = Blog.objects.all() # 列表
>>> b
<QuerySet [<Blog: javajavajava>, <Blog: docker>, <Blog: go语言>, <Blog: html超文本标记语言>, <Blog: 数据结构>, <Blo <Blog: java111>, <Blog: docker>]>
>>> b[0] # 查询第一条数据
<Blog: javajavajava>
# 2.查询前3条数据
select * from users_blog limit 3
>>> b = Blog.objects.all()[:3] # 列表截取
<QuerySet [<Blog: javajavajava>, <Blog: docker>, <Blog: go语言>]>
values()¶
values 方法,数据以列表返回,列表元素以字典表示
# 查询某个字段
select name from users_blog
>>> b = Blog.objects.values('name')
>>> b
<QuerySet [{'name': 'javajavajava'}, {'name': 'docker'}, {'name': 'go语言'}, {'name': 'html超文本标记语言'}, {'name}, {'name': 'Python算法'}, {'name': 'java111'}, {'name': 'docker'}]>
>>> b[1]['name']
'docker'
values_list()¶
数据以列表返回,列表元素以元组表示
>>> b = Blog.objects.values_list('tagline')[:3]
>>> b
<QuerySet [('全表更新',), ('全表更新',), ('全表更新',)]>
get()¶
select * from users_blog where id = 10
>>> b = Blog.objects.get(id=10)
>>> b
<Blog: 数据结构>
>>> b.name
'数据结构'
>>> b.tagline
'全表更新'
>>>
filter()¶
>>> b = Blog.objects.filter(id=10)
>>> type(b)
<class 'django.db.models.query.QuerySet'>
>>> b[0].name
'数据结构'
>>> b[0].tagline
'全表更新'
# SQL的and查询主要在filter里面添加多个查询条件
>>> b = Blog.objects.filter(tagline='全表更新',id='8')
>>> b
<QuerySet [<Blog: go语言>]>
# filter的查询条件可设为字典格式
>>> d = dict(tagline='全表更新',id='9')
>>> Blog.objects.filter(**d)
<QuerySet [<Blog: html超文本标记语言>]>
Q(field=value)|Q(field=value)¶
# SQL的or查询,需要引入Q,编写格式:Q(field=value)|Q(field=value) 多个Q 用 | 隔开
select * from users_blog where tagline='全表更新' or id = 10
>>> b = Blog.objects.filter(Q(tagline='全表更新')|Q(id=4))
>>> b
<QuerySet [<Blog: javajavajava>, <Blog: docker>, <Blog: go语言>, <Blog: html超文本标记语言>, <Blog: 数据结构>, <Blo>
# SQL的不等于查询,在Q查询前使用 ~ 符号
select * from users_blog where not tagline='全表更新'
>>> b = Blog.objects.filter(~Q(tagline='全表更新'))
>>> b
<QuerySet [<Blog: java111>, <Blog: docker>]>
exclude()¶
# 不等于查询
>>> b=Blog.objects.exclude(tagline='全表更新')
>>> b
<QuerySet [<Blog: java111>, <Blog: docker>]>
count()¶
# 统计查询数据的数据量
>>> Blog.objects.filter(tagline='全表更新').count()
6
distinct()¶
select distinct tagline from users_blog where tagline = '全表更新'
select distinct tagline from users_blog
>>> Blog.objects.values('tagline').filter(tagline='全表更新').distinct()
<QuerySet [{'tagline': '全表更新'}]>
order_by()¶
# 降序只要在order_by里面的字段家 -
>>> Blog.objects.order_by('-id')
<QuerySet [<Blog: docker>, <Blog: java111>, <Blog: Python算法>, <Blog: 数据结构>, <Blog: html超文本标记语言>, <Blogg: docker>, <Blog: javajavajava>]>
>>> Blog.objects.order_by('id')
<QuerySet [<Blog: javajavajava>, <Blog: docker>, <Blog: go语言>, <Blog: html超文本标记语言>, <Blog: 数据结构>, <Blo <Blog: java111>, <Blog: docker>]>
# order_by可以设置多字段排列
>>> Blog.objects.order_by('tagline','-id')
<QuerySet [<Blog: docker>, <Blog: java111>, <Blog: Python算法>, <Blog: 数据结构>, <Blog: html超文本标记语言>, <Blogg: docker>, <Blog: javajavajava>]>
annotate()¶
聚合查询,实现对数据值求和、求平均值。
annotate类似于SQL里面的GROUP BY方法
如果不设置values,默认对主键进行group by分组
aggregate()¶
union()¶
intersection()¶
difference()¶
常用操作示例 QuerySet¶
如果需要对这个 QuerySet 进行处理,可以参考以下操作:
- 获取所有用户名列表
usernames = [user.username for user in queryset]
print(usernames) # 输出:['json', 'john_doe', 'john_doea', 'john_doeaa', 'jason', 'admin']
- 筛选特定用户
# 获取 admin 用户
admin_user = queryset.get(username='admin')
# 筛选用户名包含 'john' 的用户
john_users = queryset.filter(username__contains='john')
- 更新用户信息
# 将所有用户的 is_active 设为 True
queryset.update(is_active=True)
- 转换为字典列表
user_dicts = queryset.values('id', 'username', 'email')
# 结果类似:[{'id': 1, 'username': 'json', 'email': 'json@example.com'}, ...]
- 获取数量
count = queryset.count() # 输出:6
如果需要对这些用户执行特定操作(如删除、批量更新、提取信息等),可以基于上述示例进一步扩展。
数据新增¶
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField(verbose_name='标语,品牌口号')
def __str__(self):
return self.name
class Author(models.Model):
name = models.CharField(max_length=200)
email = models.EmailField()
def __str__(self):
return self.name
class Entry(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateField()
mod_date = models.DateField()
authors = models.ManyToManyField(Author)
number_of_comments = models.IntegerField()
number_of_pingbacks = models.IntegerField()
rating = models.IntegerField()
def __str__(self):
return self.headline
create()¶
# 方法一 create()
>>> from users.models import *
>>> b = Blog.objects.create(name='javascript教程',tagline='前端必须语言')
>>> b.id # 获取新增数据的主键ID
2
# 方法二 create() 数据以字典格式表示
>>> b = dict(name='django==3.2',tagline='有期限的完美主义框架')
>>> b=Blog.objects.create(**b)
>>> b.id
3
save()¶
# 方法三 save()在实例化时直接设置属性值
>>> b = Blog(name='c语言',tagline='c生万物')
>>> b.save()
>>> b.id
4
# 方法四 save()
>>> b = Blog()
>>> b.name = 'python-list'
>>> b.tagline = '最好的教程'
>>> b.save()
get_or_create()¶
# get_or_create() 去重判断,确保数据不会重复插入
>>> d = dict(name='java')
>>> b =Blog.objects.get_or_create(**d)
>>> b
(<Blog: java>, False)
>>> b[0].id
5
# 只要有一个模型字段的值与数据表的数据不相同(除主键外),就会执行数据插入的操作。
# 如果每个模型的字段的值与数据表的某行数据完全相同,就不执行数据插入,而是返回这行数据的数据对象。
>>> d = dict(name='c++')
>>> Blog.objects.get_or_create(**d)
(<Blog: c++>, True)
>>> Blog.objects.get_or_create(**d)
(<Blog: c++>, False)
>>>
update_or_create()¶
判断当前数据在表里是否存在,若存在,则进行更新操作,否则在数据表里新增数据
# 新增
>>> d=dict(name='docker怎么玩')
>>> b = Blog.objects.update_or_create(**d)
>>> b
(<Blog: docker怎么玩>, True)
>>> b[0].id
7
# 修改
>>> b = Blog.objects.update_or_create(**d,defaults={'name':'docker'})
>>> b
(<Blog: docker>, False)
>>> b[0].name
'docker'
bulk_create()¶
批量插入,将数据对象以列表或元组的形式传入bulk_create()
# 列表
>>> b1 = Blog(name='go语言')
>>> b2 = Blog(name='html超文本标记语言')
>>> object_list = [b1,b2]
>>> Blog.objects.bulk_create(object_list)
[<Blog: go语言>, <Blog: html超文本标记语言>]
>>>
# 元组
>>> t1 = Blog(name='数据结构')
>>> t2 = Blog(name='Python算法')
>>> object_tuple = (t1,t2)
>>> Blog.objects.bulk_create(object_tuple)
[<Blog: 数据结构>, <Blog: Python算法>]
数据修改¶
save()¶
>>> b = Blog.objects.get(id=5) # get查询方法
>>> b.tagline = '一次编译到处运行'
>>> b.save()
update()¶
# 批量更新一条或多条数据,查询方法使用filter
# filter 以列表格式返回,查询结果可能时一条或多条数据
>>> Blog.objects.filter(name='c++').update(name='filter().update()后c++')
1
# 更新数据以字典格式表示
>>> d = dict(name='javajavajava')
>>> Blog.objects.filter(id=6).update(**d)
1
# 不使用查询方法,默认对全表数据进行更新
>>> Blog.objects.update(tagline='全表更新')
11
# 使用内置F方法实现数据的自增或自减
# F方法还可以在annotate或filter方法里使用
>>> b= Blog.objects.filter(id=11) # 把id=11 加一
>>> b.update(id=F('id')+1)
1
bulk_update()¶
批量更新
# 新增两行数据
>>> b1 = Blog.objects.create(name='java',tagline='一次编译到处运行')
>>> b2 = Blog.objects.create(name='docker',tagline='>>> b= Blog.objects.filter(id=11)')
# 修改字段 name 和 tagline
>>> b1.name='java111'
>>> b2.tagline='Securely build, share and run any application, anywhere'
# 批量修改字段 name 和 tagline
>>> Blog.objects.bulk_update([b1,b2],fields=['name','tagline'])
数据删除¶
delete()¶
删除数据有3中方式:删除数据表的全数据、删除一行数据和删除多行数据
# 删除数据表中的全部数据
>>> Author.objects.all().delete()
(1, {'users.Author': 1})
# 删除一条id为1的数据
>>> Blog.objects.get(id=1).delete()
(1, {'users.Blog': 1})
# 删除多条数据
>>> Blog.objects.filter(tagline='全表更新1').delete()
(4, {'users.Blog': 4})
# 删除数据的过程中,如果删除的数据设有外键字段,就会同时删除外键关联的数据。
数据导出导入¶
# 导出
python manage.py dumpdata > data.json
# 导入
python manage.py loaddata data.json
在 Django 中,模型继承(Model Inheritance)是一种代码复用和模型关系设计的重要方式,允许你创建一个基础模型,然后让其他模型继承它的字段和方法。Django 提供了三种主要的模型继承方式,每种方式适用于不同的场景。¶
1. 抽象基类继承(Abstract Base Classes)¶
这是最常用的继承方式,适用于多个模型共享相同字段或方法的场景。抽象基类**不会创建实际的数据表**,只是作为其他模型的模板。
用法:¶
- 在基类中设置
abstract = True
使其成为抽象类 - 子类会继承基类的所有字段和方法
- 每个子类会生成独立的数据表,包含自身字段 + 继承的字段
from django.db import models
# 抽象基类(不会创建数据表)
class BaseModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True) # 自动记录创建时间
updated_at = models.DateTimeField(auto_now=True) # 自动记录更新时间
is_active = models.BooleanField(default=True) # 逻辑删除标记
class Meta:
abstract = True # 关键:声明为抽象类
# 子类1:继承抽象基类
class Article(BaseModel):
title = models.CharField(max_length=100)
content = models.TextField()
# 子类2:继承抽象基类
class Comment(BaseModel):
article = models.ForeignKey(Article, on_delete=models.CASCADE)
text = models.CharField(max_length=200)
特点:¶
- 抽象类本身不会生成数据表
- 子类的数据表包含自身字段 + 基类所有字段(如
created_at
会出现在Article
和Comment
表中) - 适用于提取多个模型的公共字段(如时间戳、状态标记等)
2. 多表继承(Multi-table Inheritance)¶
每个模型(包括父类)都会生成独立的数据表,子类会自动创建一个指向父类的一对一关系(OneToOneField)。适用于需要单独访问父类实例的场景。
用法:¶
- 父类是普通模型(不设置
abstract
) - 子类直接继承父类,Django 会自动在子类中添加一个指向父类的
OneToOneField
(字段名为parent_ptr
)
from django.db import models
# 父类:普通模型(会生成数据表)
class Person(models.Model):
name = models.CharField(max_length=50)
age = models.IntegerField()
# 子类:继承自 Person(会生成独立数据表)
class Student(Person):
school = models.CharField(max_length=100)
grade = models.IntegerField()
底层实现:¶
- Django 会为
Person
和Student
分别创建数据表 Student
表中会自动添加person_ptr
字段(关联Person
表的主键)- 子类实例可以访问父类的所有字段(如
student.name
、student.age
)
特点:¶
- 父类和子类都有独立的数据表,可单独查询
- 子类通过
parent_ptr
与父类关联,本质是一对一关系 - 适用于 "is-a" 关系(如
Student
是一种Person
)
3. 代理模型(Proxy Models)¶
代理模型不会创建新的数据表,而是为现有模型创建一个 "代理",用于修改模型的行为(如默认排序、新增方法等),但不改变原有字段。
用法:¶
- 在子类中设置
proxy = True
- 代理模型与原模型共享同一张数据表
- 可重写
Meta
选项或添加新方法,但不能添加新字段
from django.db import models
# 原模型
class Book(models.Model):
title = models.CharField(max_length=100)
publish_date = models.DateField()
class Meta:
ordering = ['title'] # 默认按标题排序
# 代理模型:修改排序方式,添加新方法
class RecentBook(Book):
class Meta:
proxy = True # 声明为代理模型
ordering = ['-publish_date'] # 按发布日期倒序(最新的在前)
def is_recent(self):
"""判断是否是近30天内发布的书籍"""
from datetime import timedelta, date
return self.publish_date >= date.today() - timedelta(days=30)
特点:¶
- 与原模型共享数据(操作
RecentBook
等同于操作Book
) - 可自定义排序、管理器(Manager)或方法,但不能添加新字段
- 适用于需要对同一批数据提供不同访问方式的场景
继承方式的选择建议¶
- 抽象基类:多个模型共享字段时使用(最常用)
- 多表继承:需要父类和子类各自独立存在,且有 "is-a" 关系时使用
- 代理模型:仅需修改模型行为(排序、方法等)而不改变结构时使用
注意事项¶
- 多表继承可能导致查询效率降低(需关联多个表)
- 抽象类的
Meta
选项不会被子类完全继承,需显式声明(如ordering
) - 代理模型不能添加新字段,若需扩展字段应使用多表继承或一对一关系
在 Django 的抽象基类(Abstract Base Classes)继承中,基类的 Meta
类不会被子类完全继承,但会有部分特殊处理规则。¶
具体来说:
- 默认不继承完整的
Meta
配置 子类不会自动继承抽象基类的Meta
类中的配置(如ordering
、verbose_name
等)。如果子类需要使用基类的Meta
配置,必须显式声明继承。
class BaseModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
abstract = True
ordering = ['-created_at'] # 基类的排序配置
verbose_name = "基础模型"
# 子类1:未显式继承基类Meta,不会获得ordering和verbose_name
class Article(BaseModel):
title = models.CharField(max_length=100)
# 未定义Meta,使用Django默认配置
# 子类2:显式继承基类Meta
class Comment(BaseModel):
content = models.TextField()
class Meta(BaseModel.Meta): # 显式继承基类的Meta
verbose_name = "评论" # 覆盖基类的verbose_name
# 会继承基类的ordering配置
- 特殊字段的继承规则
抽象基类
Meta
中的abstract = True
不会被子类继承(子类默认是普通模型,除非自己也声明abstract = True
)。
此外,Meta
中的 unique_together
、indexes
等与数据库结构相关的配置,若子类显式继承基类 Meta
,则会被继承并可叠加。
-
总结
-
抽象基类的
Meta
配置**不会自动被子类继承**。 - 子类若需复用基类的
Meta
配置,必须通过class Meta(BaseModel.Meta)
显式继承。 - 显式继承后,子类可以覆盖基类
Meta
中的特定配置(如verbose_name
),未覆盖的则沿用基类配置。
这种设计允许子类灵活定制自己的元数据,同时保留复用基类配置的能力。
在 Django 模型中,Meta
类(元类)是一个嵌套在模型类内部的特殊类,用于定义模型的**元数据**(即模型本身的配置信息)。这些配置会影响模型的行为、数据库表结构以及与数据库交互的方式。¶
通过 Meta
类,你可以自定义模型的各种特性,而无需修改模型的字段或方法。
常用的 Meta
配置项¶
1. 数据库表名相关¶
db_table
:指定模型对应的数据库表名(默认表名是 “应用名_模型类名小写”)
class Book(models.Model):
title = models.CharField(max_length=100)
class Meta:
db_table = "my_books" # 自定义表名为 my_books
ordering
:指定查询模型时的默认排序方式(接受字段名列表,字段前加-
表示倒序)
class Article(models.Model):
title = models.CharField(max_length=100)
publish_date = models.DateTimeField()
class Meta:
ordering = ['-publish_date', 'title'] # 先按发布时间倒序,再按标题正序
2. 模型行为相关¶
-
verbose_name
:模型的 “友好名称”(单数形式,用于后台管理等场景) -
verbose_name_plural
:模型的复数形式友好名称(默认是verbose_name + 's'
)
class Person(models.Model):
name = models.CharField(max_length=50)
class Meta:
verbose_name = "人员"
verbose_name_plural = "人员列表" # 复数形式避免默认加 's'
abstract
:声明模型为抽象基类(用于继承,不会生成数据表)
class BaseModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
abstract = True # 抽象类标记
proxy
:声明为代理模型(与原模型共享数据表,仅修改行为)
class RecentArticle(Article):
class Meta:
proxy = True # 代理模型标记
3. 权限与管理相关¶
permissions
:自定义模型的权限(Django 默认会生成 “添加、修改、删除” 权限)
class Document(models.Model):
content = models.TextField()
class Meta:
permissions = [
("can_publish", "Can publish documents"), # 自定义“发布”权限
("can_archive", "Can archive documents"), # 自定义“归档”权限
]
default_related_name
:设置反向关联的默认名称(替代默认的model_set
)
class Author(models.Model):
name = models.CharField(max_length=50)
class Book(models.Model):
author = models.ForeignKey(Author, on_delete=models.CASCADE)
class Meta:
default_related_name = "books" # 反向关联可通过 author.books 访问(替代 author.book_set)
4. 数据库约束相关¶
unique_together
:指定多个字段的组合唯一约束(确保组合值不重复)
class Student(models.Model):
name = models.CharField(max_length=50)
class_name = models.CharField(max_length=20)
class Meta:
unique_together = [['name', 'class_name']] # 同一班级不能有同名学生
indexes
:定义模型的索引(优化查询性能)
class Product(models.Model):
name = models.CharField(max_length=100)
price = models.DecimalField(max_digits=10, decimal_places=2)
class Meta:
indexes = [
models.Index(fields=['name']), # 为 name 字段创建索引
models.Index(fields=['-price']), # 为 price 字段创建倒序索引
]
5. 其他常用配置¶
managed
:控制 Django 是否管理该模型的数据表(False
表示不自动创建 / 删除表)
class LegacyTable(models.Model):
# 映射已有数据库表,不希望 Django 自动修改表结构
class Meta:
managed = False
db_table = "legacy_table" # 关联已存在的表
order_with_respect_to
:用于关联模型的排序(通常配合外键使用)
class Answer(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
content = models.TextField()
class Meta:
order_with_respect_to = 'question' # 相对于 question 排序
注意事项¶
Meta
类是可选的,若不定义则使用 Django 默认配置。- 抽象基类的
Meta
配置不会被完全继承,子类需要显式重写需要的配置(如ordering
)。 - 部分配置(如
db_table
、indexes
)会影响数据库结构,修改后需执行迁移(makemigrations
和migrate
)。 - 合理使用
Meta
可以简化代码、优化性能,并使模型更符合业务需求。
通过 Meta
类,你可以灵活地定制 Django 模型的各种行为,是 Django ORM 中非常重要的特性。
CORS¶
Help
Reason given for failure:
Origin checking failed - http://192.168.1.24 does not match any trusted origins.
In general, this can occur when there is a genuine Cross Site Request Forgery, or when Django’s CSRF mechanism has not been used correctly. For POST forms, you need to ensure:
Your browser is accepting cookies.
The view function passes a request to the template’s render method.
In the template, there is a {% csrf_token %} template tag inside each POST form that targets an internal URL.
If you are not using CsrfViewMiddleware, then you must use csrf_protect on any views that use the csrf_token template tag, as well as those that accept the POST data.
The form has a valid CSRF token. After logging in in another browser tab or hitting the back button after a login, you may need to reload the page with the form, because the token is rotated after a login.
You’re seeing the help section of this page because you have DEBUG = True in your Django settings file. Change that to False, and only the initial error message will be displayed.
You can customize this page using the CSRF_FAILURE_VIEW setting.
因素 | JWT 认证 | django-cors-headers + CSRF |
---|---|---|
跨域处理复杂度 | 低(无需处理 CSRF) | 高(需配置 CORS、CSRF、Cookie) |
安全性 | 需防范 Token 泄露(如 HTTPS) | 依赖 Django 原生 CSRF 保护 |
性能 | Token 验证开销(但可缓存) | Session 存储开销(如 Redis) |
移动端支持 | 最佳(无 Cookie 限制) | 较差(需处理 Cookie 同步) |
开发效率 | 高(库成熟,配置简单) | 中(需处理 CSRF 与 CORS 冲突) |
# django-cors-headers API 开发使用不多 了解
# JWT 认证(JSON Web Token)
django-rest-framework-simplejwt
curl \
-X POST \
-H "Content-Type: application/json" \
-d '{"refresh":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX3BrIjoxLCJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImNvbGRfc3R1ZmYiOiLimIMiLCJleHAiOjIzNDU2NywianRpIjoiZGUxMmY0ZTY3MDY4NDI3ODg5ZjE1YWMyNzcwZGEwNTEifQ.aEoAYkSJjoWH1boshQAaTkf8G3yn0kapko6HFRt7Rh4"}' \
http://127.0.0.1:8000/api/token/refresh/
curl \
-X POST \
-H "Content-Type: application/json" \
-d '{"username": "davidattenborough", "password": "boatymcboatface"}' \
http://127.0.0.1:8000/api/token/
HTTP协议¶
响应¶
HttpOnly 详解¶
HttpOnly
是一个 HTTP 响应头属性,用于标记 Cookie,目的是增强 Web 应用的安全性,主要防御 跨站脚本攻击(XSS) 对 Cookie 的窃取。
1. 基本概念¶
当服务器在设置 Cookie 时添加 HttpOnly
属性后:
- 客户端脚本(如 JavaScript)无法读取或修改该 Cookie(通过
document.cookie
无法访问)。 - 该 Cookie 仅会在浏览器发起 HTTP/HTTPS 请求时自动携带(如请求头的
Cookie
字段),由浏览器和服务器直接交互。
2. 作用与安全意义¶
主要用于保护 敏感 Cookie(如会话 ID、认证令牌、JWT 的 refresh_token 等),防止被 XSS 攻击窃取:
- XSS 攻击原理:攻击者注入恶意 JavaScript 代码到网页,通过
document.cookie
读取用户 Cookie,进而伪造身份登录。 - HttpOnly 防御逻辑:禁止 JS 访问 Cookie,即使页面被注入恶意脚本,也无法窃取标记了
HttpOnly
的 Cookie。
3. 如何设置 HttpOnly¶
在服务器端设置 Cookie 时,通过响应头的 Set-Cookie
字段添加 HttpOnly
属性即可。
Django 原生实现 vs DRF 框架实现的核心区别¶
特性 | Django 原生实现 | DRF 框架实现 |
---|---|---|
代码量 | 需要手动编写序列化 / 反序列化逻辑、状态码处理、请求方法判断 | 基于 ModelSerializer 和 ViewSet,大幅减少重复代码 |
序列化 | 需手动编写字典转换逻辑,处理数据验证 | 通过 ModelSerializer 自动生成序列化逻辑,内置数据验证 |
视图功能 | 需手动实现 get/post/put/delete 等方法 | ViewSet 自动提供 CRUD 操作,支持多种视图类型 |
认证授权 | 需手动实现认证逻辑 | 内置多种认证方式(Token、JWT 等),配置即可用 |
API 文档 | 无内置文档,需手动编写 | 集成 Swagger/ReDoc,自动生成交互式 API 文档 |
查询功能 | 需手动实现过滤、排序 | 内置过滤、排序、分页功能,简单配置即可使用 |
响应处理 | 需手动设置状态码和响应格式 | 自动处理不同状态码,支持多种响应格式(JSON、XML 等) |
扩展性 | 扩展功能需从零开始实现 | 提供丰富的扩展点和插件生态 |
部署Django¶
# 查看Linux系统版本
lsb_release -a
Distributor ID: Ubuntu
Description: Ubuntu 22.04.5 LTS
Release: 22.04
Codename: jammy
python环境搭建¶
sudo apt -y install python-is-python3 python3.10-venv python3-pip
# 创建虚拟环境
cd ~
mkdir .venv
cd .venv
python -m venv web
. ./web/bin/activate
# 安装Python依赖包
pip install -r requirements.txt
gunicorn¶
# 配置gunicorn
pip install gunicorn
# 创建Gunicorn服务文件
sudo vim /etc/systemd/system/gunicorn.service
# 添加内容
[Unit]
Description=gunicorn daemon
After=network.target
[Service]
User=leo
Group=leo
WorkingDirectory=/home/leo/Desktop/thesis/bec
ExecStart=/home/leo/.venv/web/bin/gunicorn --workers 3 --bind unix:/tmp/192.168.68.130.socket bec.wsgi:application
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl start gunicorn
sudo systemctl enable gunicorn
# 重新加载systemd并启动Gunicorn服务
Warning: The unit file, source configuration file or drop-ins of gunicorn.service changed on disk. Run 'systemctl daemon-reload' to reload units.
警告:gunicorn的单元文件、源配置文件或插件。磁盘上的服务已更改。运行'systemctl daemon-reload'重新加载单元。
Created symlink /etc/systemd/system/multi-user.target.wants/gunicorn.service → /etc/systemd/system/gunicorn.service.
# 修改文件
sudo systemctl daemon-reload
sudo systemctl restart gunicorn.service
sudo systemctl status gunicorn.service
journalctl -u gunicorn.service -f
ps -ef | grep gunicorn
nginx¶
sudo vim /etc/nginx/sites-available/192.168.68.130
server {
listen 80;
server_name 192.168.68.130;
location = /favicon.ico { access_log off; log_not_found off;}
location /static/ {
alias /home/leo/Desktop/thesis/bec;
}
location / {
include proxy_params;
proxy_pass http://unix:/tmp/192.168.68.130.socket;
}
}
sudo ln -s /etc/nginx/sites-available/192.168.68.130 /etc/nginx/sites-enabled
sudo systemctl status gunicorn
sudo systemctl status nginx
同源策略
CSRF
进行 Django API 开发时,以下是必须掌握的核心技术和知识点:¶
- Django 基础 模型层(ORM):定义数据模型、关系和迁移。 视图层:处理请求和业务逻辑(基于函数或类的视图)。 URL 路由:映射 URL 到视图函数 / 类。 序列化:将模型数据转换为 JSON/XML 等格式。
- Django REST Framework (DRF) 序列化器(Serializers):验证和转换数据。 视图集(ViewSets):快速实现 CRUD 操作。 路由(Routers):自动生成 API URL。 权限(Permissions):控制 API 访问(如IsAuthenticated)。 认证(Authentication):处理用户认证(Token、JWT 等)。
- API 设计原则 RESTful 规范:URL 设计、HTTP 方法使用、状态码定义。 版本控制:处理 API 版本变更(如 URL 参数、请求头)。 请求与响应格式:JSON 结构、错误处理。
- 数据库与迁移 SQL 基础:查询、索引、事务。 数据库设计:表结构、关系优化。 Django 迁移:管理数据库变更。
- 认证与授权 用户认证:基于 Token 或 JWT 的认证。 权限控制:基于角色的访问控制(RBAC)。 安全头:设置 CORS、CSRF 保护。
- 测试与调试 单元测试:使用django.test或pytest。 API 测试工具:Postman、curl、HTTPie。 调试技巧:日志记录、断点调试。
- 部署与性能优化 WSGI/ASGI 服务器:Gunicorn、Uvicorn。 容器化:Docker、Docker Compose。 缓存:Redis、Memcached。 中间件:性能监控、限流。