Skip to content

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 进行处理,可以参考以下操作:

  1. 获取所有用户名列表
usernames = [user.username for user in queryset]
print(usernames)  # 输出:['json', 'john_doe', 'john_doea', 'john_doeaa', 'jason', 'admin']
  1. 筛选特定用户
# 获取 admin 用户
admin_user = queryset.get(username='admin')

# 筛选用户名包含 'john' 的用户
john_users = queryset.filter(username__contains='john')
  1. 更新用户信息
# 将所有用户的 is_active 设为 True
queryset.update(is_active=True)
  1. 转换为字典列表
user_dicts = queryset.values('id', 'username', 'email')
# 结果类似:[{'id': 1, 'username': 'json', 'email': 'json@example.com'}, ...]
  1. 获取数量
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 会出现在 ArticleComment 表中)
  • 适用于提取多个模型的公共字段(如时间戳、状态标记等)

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 会为 PersonStudent 分别创建数据表
  • Student 表中会自动添加 person_ptr 字段(关联 Person 表的主键)
  • 子类实例可以访问父类的所有字段(如 student.namestudent.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)或方法,但不能添加新字段
  • 适用于需要对同一批数据提供不同访问方式的场景

继承方式的选择建议

  1. 抽象基类:多个模型共享字段时使用(最常用)
  2. 多表继承:需要父类和子类各自独立存在,且有 "is-a" 关系时使用
  3. 代理模型:仅需修改模型行为(排序、方法等)而不改变结构时使用

注意事项

  • 多表继承可能导致查询效率降低(需关联多个表)
  • 抽象类的 Meta 选项不会被子类完全继承,需显式声明(如 ordering
  • 代理模型不能添加新字段,若需扩展字段应使用多表继承或一对一关系

在 Django 的抽象基类(Abstract Base Classes)继承中,基类的 Meta 类不会被子类完全继承,但会有部分特殊处理规则。

具体来说:

  1. 默认不继承完整的 Meta 配置 子类不会自动继承抽象基类的 Meta 类中的配置(如 orderingverbose_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配置
  1. 特殊字段的继承规则 抽象基类 Meta 中的 abstract = True 不会被子类继承(子类默认是普通模型,除非自己也声明 abstract = True)。

此外,Meta 中的 unique_togetherindexes 等与数据库结构相关的配置,若子类显式继承基类 Meta,则会被继承并可叠加。

  1. 总结

  2. 抽象基类的 Meta 配置**不会自动被子类继承**。

  3. 子类若需复用基类的 Meta 配置,必须通过 class Meta(BaseModel.Meta) 显式继承。
  4. 显式继承后,子类可以覆盖基类 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 排序

注意事项

  1. Meta 类是可选的,若不定义则使用 Django 默认配置。
  2. 抽象基类的 Meta 配置不会被完全继承,子类需要显式重写需要的配置(如 ordering)。
  3. 部分配置(如 db_tableindexes)会影响数据库结构,修改后需执行迁移(makemigrationsmigrate)。
  4. 合理使用 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 开发时,以下是必须掌握的核心技术和知识点:

  1. Django 基础 模型层(ORM):定义数据模型、关系和迁移。 视图层:处理请求和业务逻辑(基于函数或类的视图)。 URL 路由:映射 URL 到视图函数 / 类。 序列化:将模型数据转换为 JSON/XML 等格式。
  2. Django REST Framework (DRF) 序列化器(Serializers):验证和转换数据。 视图集(ViewSets):快速实现 CRUD 操作。 路由(Routers):自动生成 API URL。 权限(Permissions):控制 API 访问(如IsAuthenticated)。 认证(Authentication):处理用户认证(Token、JWT 等)。
  3. API 设计原则 RESTful 规范:URL 设计、HTTP 方法使用、状态码定义。 版本控制:处理 API 版本变更(如 URL 参数、请求头)。 请求与响应格式:JSON 结构、错误处理。
  4. 数据库与迁移 SQL 基础:查询、索引、事务。 数据库设计:表结构、关系优化。 Django 迁移:管理数据库变更。
  5. 认证与授权 用户认证:基于 Token 或 JWT 的认证。 权限控制:基于角色的访问控制(RBAC)。 安全头:设置 CORS、CSRF 保护。
  6. 测试与调试 单元测试:使用django.test或pytest。 API 测试工具:Postman、curl、HTTPie。 调试技巧:日志记录、断点调试。
  7. 部署与性能优化 WSGI/ASGI 服务器:Gunicorn、Uvicorn。 容器化:Docker、Docker Compose。 缓存:Redis、Memcached。 中间件:性能监控、限流。