(七)外键、多对多与中间表
一本书只属于一个用户,一个用户可以有多本书,ForeignKey 在书籍表保存 owner_id。book.owner 返回关联用户对象,book.owner_id 直接读取编号。on_delete=CASCADE 表示用户删除时,一并删除它的书。
一本书可以有多个标签,同一个“工程”标签也可以用于不同的书。ManyToManyField 创建关联中间表,每行保存书籍编号与标签编号。标签在这章作为共享分类名称;用户的具体书籍和备注仍按归属隔离。
book.tags.add(tag) 添加关联,book.tags.set(tags) 把当前关联替换为这份列表。中间表需要两侧主键,因此新书先 save() 取得编号,再保存标签。下面的实际演示也检查模型字段:
# 项目文件:demos/model_steps.py
from lab import lab
with lab():
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from library.models import Book, Tag
alice = get_user_model().objects.get(username="alice")
book = Book(owner=alice, title="ORM 实践")
print("构造后", book.pk)
book.full_clean() # 主动执行模型校验;save 本身不会自动调用它。
book.save()
print("保存后有编号", book.pk is not None)
tag, created = Tag.objects.get_or_create(name="框架")
book.tags.add(tag) # 取得书籍编号后,才能保存中间表的关联。
print("关联标签", list(book.tags.values_list("name", flat=True)))
invalid = Book(owner=alice, title="x" * 101)
try:
invalid.full_clean()
except ValidationError as error:
print("模型错误字段", list(error.message_dict))save() 负责持久化,不会自动调用 full_clean()。表单会执行相关校验,直接用 ORM 导入数据时可以主动调用 full_clean;组合唯一约束还会由数据库在实际写入时检查。