Django模型字段类型与选项:ForeignKey on_delete
在Django中,模型是数据描述的准确载体,每个模型映射一张数据库表,模型类继承自 django.db.models.Model,其属性对应数据库字段。Django 通过这些字段类自动生成数据库访问 API,并指定数据库数据类型,提供表单渲染和基础验证。一、常用字段类型
AutoField:自增整数字段,根据可用 ID 自动递增。BigAutoField:64位自增整数,范围 1 到 9223372036854775807。BooleanField:布尔字段,默认表单部件为 CheckboxInput(null=True 时为 NullBooleanSelect),默认值为 None。CharField:字符串字段,适用于中小型文本,需指定 max_length(必填)和可选的 db_collation。DateField:日期字段,对应 datetime.date 实例,支持 auto_now(每次保存时更新为当前时间)和 auto_now_add(创建时设为当前时间)参数。
二、字段选项
• null:设为 True 时,空字段在数据库中存为 NULL,默认 False。
• blank:设为 True 时,字段允许为空,默认 False。
• choices:二元组列表,用于提供选项,例如 CHOICES = [('FR', 'Freshman'), ('SO', 'Sophomore')]。
• default:字段默认值。
• help_text:表单控件显示的帮助文本。
• primary_key:设为 True 时该字段成为主键。
• unique:值为 True 时字段在整个表中唯一。
除 ForeignKey、ManyToManyField、OneToOneField 外,任何字段均可接受 verbose_name 参数,未指定时自动将属性名下划线替换为空格。
三、关联关系
Django 支持三种数据库关联:多对一(ForeignKey)、多对多(ManyToManyField)、一对一(OneToOneField)。
Foreignkey 需要两个位置参数:关联的模型类(或 'self' 表示自关联)和 on_delete 参数。若关联尚未定义的模型,可使用模型名称字符串。
示例:Car 与 Manufacturer 的多对一关系
from django.db import models
class Manufacturer(models.Model):
# ...
pass
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
# ...
四、on_delete 参数详解
当被引用的对象被删除时,on_delete 决定 ForeignKey 的行为,可选值在 django.db.models 中:
• CASCADE:级联删除,被引用的对象删除时,同时删除包含 ForeignKey 的对象。
• PROTECT:通过引发 ProtectedError(IntegrityError 子类)阻止删除被引用的对象。
• RESTRICT(Django 3.1+):通过引发 RestrictedError 防止删除,但若被引用对象在同一操作中被 CASCADE 关联删除则允许。
• SET_NULL:将 ForeignKey 设为 NULL,需同时指定 null=True。
• SET_DEFAULT:设为默认值,需为 ForeignKey 设置 default。
• SET():设为指定值或可调用对象的返回值。
• DO_NOTHING:不采取措施,由数据库自身约束处理。
示例:
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.SET_NULL, null=True)
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.SET_DEFAULT, default=1)
def get_manufacturer():
return 1
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.SET(get_manufacturer))
五、地区自关联实战
以地区表自关联为例,模型中使用自引用 ForeignKey:
class District(models.Model):
parent = models.ForeignKey(
"self",
null=True,
blank=True,
on_delete=models.CASCADE,
related_name="child",
verbose_name="父级地区",
)
name = models.CharField(max_length=128, help_text="地区名称", verbose_name="地区名称")
level = models.IntegerField(default=0, help_text="地区级别", verbose_name="地区级别")
full_name = models.CharField(max_length=256, help_text="地区全名", verbose_name="地区全名")
def __str__(self):
return self.full_name
class Meta:
managed = True
verbose_name = "地区信息"
verbose_name_plural = verbose_name
配合 DRF 视图实现父级下二级地区查询,并接入缓存(Redis 等):
class CityCountyView(APIView):
authentication_classes =
permission_classes = (AllowAny,)
def get(self, request, pk):
# 缓存键
cache_key = 'sub_data_%s' % pk
sub_data = cache.get(cache_key)
if not sub_data:
try:
parent_obj = District.objects.get(id=pk)
sub_objs = parent_obj.child.all()
subs = []
for sub in sub_objs:
subs.append({'id': sub.id, 'name': sub.name})
sub_data = {'id': pk, 'name': parent_obj.name, 'subs': subs}
except Exception as e:
print(e)
return Response({'code': 400, 'errmsg': '查询失败'})
# 缓存一天
cache.set(cache_key, sub_data, 3600 * 24)
return Response({'code': 0, 'errmsg': 'ok', 'sub_data': sub_data})
六、动态生成模型类
有时需要根据不同参数动态创建模型。以下两种方式均可实现:
方式一:函数返回内部类
def getTaskDetailModel(table_name):
class MyClass(models.Model):
task = models.ForeignKey(Task, on_delete=models.CASCADE, related_name='details', verbose_name='任务')
user_id = models.IntegerField(verbose_name='用户ID')
created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
is_complet = models.BooleanField(default=False, verbose_name='是否完成')
class Meta:
db_table = table_name
return MyClass
方式二:使用 type() 动态创建
def create_task_detail_model(password_code):
classname = f'TaskDetail_{password_code}'
class Meta:
db_table = classname
return type(classname, (models.Model,), {
'task': models.ForeignKey(Task, on_delete=models.CASCADE, related_name='details', verbose_name='任务'),
'user_id': models.IntegerField(verbose_name='用户ID'),
'created_time': models.DateTimeField(auto_now_add=True, verbose_name='创建时间'),
'is_complet': models.BooleanField(default=False, verbose_name='是否完成'),
'__str__': lambda self: f"{self.task.password_code} - {self.user_id}",
'Meta': Meta,
'__module__': __name__,
})
通过以上内容,可以系统地掌握 Django 模型字段类型的选择、字段选项配置、ForeignKey 关联及 on_delete 参数的应用,并学会自关联和动态模型生成的实战技巧。
Re: Django模型字段类型与选项:ForeignKey on_delete
您好!感谢分享这么详细的技术内容,把 Django 模型字段和 ForeignKey 的 `on_delete` 参数讲得很透彻。特别是对 CASCADE、PROTECT、RESTRICT、SET_NULL 等选项的对比,以及自关联地区的实战示例,非常实用。之前我对 RESTRICT 和 PROTECT 的区别理解得不够深,看了您的解释清楚多了。期待更多这样的干货!Re: Django模型字段类型与选项:ForeignKey on_delete
感谢楼主分享关于Django模型字段和ForeignKey on_delete参数的详细说明,特别是地区自关联的实战示例,很有参考价值。对于on_delete的各个选项,你讲解得很清晰,尤其是RESTRICT在3.1+版本中的行为变化,这个细节容易被忽略。请问在实际项目中,你更倾向于使用CASCADE还是SET_NULL?另外,自关联中使用related_name='child'来反向查询子级地区,配合缓存实现二级查询,这个设计很实用。Re: Django模型字段类型与选项:ForeignKey on_delete
楼主的整理很详尽,对Django新手来说是很清晰的入门指引。我特别想补充一点关于 `on_delete` 选择时的实际考虑: - **CASCADE** 适合强关联数据,比如订单明细和订单,订单删除明细自然该删; - **PROTECT** 和 **RESTRICT** 建议在业务逻辑中要保护好关键数据时使用,比如省市区数据被引用时不让误删; - **SET_NULL** 和 **SET_DEFAULT** 适合弱关联场景,比如文章的作者被删除后,文章保留但作者变空或变成默认值。 地区自关联的例子很实用,配合DRF和缓存做树形结构接口是经典做法。另外提一个小建议:自关联里 `related_name='child'` 后,用 `parent.child.all()` 就能拿子级,查询起来非常方便,值得新朋友留意这个命名习惯。
页:
[1]