一、模型关系基础概念
1. 什么是模型关系?
模型关系是 Django ORM 的核心机制,用于描述数据库中不同数据表之间的关联方式。Django 支持三种基础关系类型:
关系类型 | 数据库实现 | ORM 字段类型 | 典型场景 |
---|
一对一关系 | 唯一外键约束 | OneToOneField | 用户扩展资料、权限分离 |
一对多关系 | 外键约束 | ForeignKey | 文章与评论、订单与商品 |
多对多关系 | 中间表 | ManyToManyField | 标签系统、用户关注关系 |
2. 关系设计核心要素
author = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='articles',
related_query_name='article',
verbose_name='作者',
db_index=True
)
二、一对一关系(OneToOne)
1. 核心特点
- 唯一性约束:确保两个模型实例一一对应
- 双向访问:可通过双方模型互相访问
- 数据分离:适合敏感数据隔离(如用户基础信息与隐私信息)
2. 应用场景
3. 实现方式
class User(models.Model):
username = models.CharField(max_length=30)
email = models.EmailField(unique=True)
class UserProfile(models.Model):
user = models.OneToOneField(
User,
on_delete=models.CASCADE,
primary_key=True
)
phone = models.CharField(max_length=15)
ssn = models.CharField(max_length=20)
user = User.objects.get(pk=1)
profile = user.userprofile
user_from_profile = profile.user
三、一对多关系(ForeignKey)
1. 核心特点
- 多方存储外键:在"多"的一方存储关联ID
- 级联操作:支持 CASCADE, SET_NULL 等策略
- 查询优化:支持 select_related 预加载
2. 应用场景
3. 实现方式
class Blog(models.Model):
title = models.CharField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
class Comment(models.Model):
blog = models.ForeignKey(
Blog,
on_delete=models.CASCADE,
related_name='comments',
verbose_name='所属文章'
)
content = models.TextField()
author = models.ForeignKey(
User,
on_delete=models.SET_NULL,
null=True
)
comments = Comment.objects.filter(blog_id=1).select_related('author')[:10]
blogs = Blog.objects.filter(
comments__content__icontains='django'
).distinct()
四、多对多关系(ManyToMany)
1. 核心特点
- 自动中间表:Django 自动生成关联表
- 双向查询:支持正反向访问
- 扩展能力:可通过 through 参数自定义中间模型
2. 应用场景
3. 基础实现
class Article(models.Model):
title = models.CharField(max_length=200)
tags = models.ManyToManyField(
'Tag',
related_name='articles'
)
class Tag(models.Model):
name = models.CharField(max_length=50, unique=True)
article = Article.objects.get(id=1)
python_tag = Tag.objects.get(name='Python')
article.tags.add(python_tag)
articles = Article.objects.filter(tags__name='Python')
4. 自定义中间表
class Student(models.Model):
name = models.CharField(max_length=100)
class Course(models.Model):
title = models.CharField(max_length=200)
students = models.ManyToManyField(
Student,
through='Enrollment',
through_fields=('course', 'student')
)
class Enrollment(models.Model):
student = models.ForeignKey(Student, on_delete=models.CASCADE)
course = models.ForeignKey(Course, on_delete=models.CASCADE)
enroll_date = models.DateField()
grade = models.DecimalField(max_digits=5, decimal_places=2)
class Meta:
unique_together = [('student', 'course')]
五、高级关系模式
1. 自关联关系
class Employee(models.Model):
name = models.CharField(max_length=100)
manager = models.ForeignKey(
'self',
on_delete=models.SET_NULL,
null=True,
blank=True
)
subordinates = models.ManyToManyField(
'self',
symmetrical=False,
through='TeamRelationship'
)
class TeamRelationship(models.Model):
leader = models.ForeignKey(
Employee,
related_name='leading_teams',
on_delete=models.CASCADE
)
member = models.ForeignKey(
Employee,
related_name='member_teams',
on_delete=models.CASCADE
)
join_date = models.DateField()
2. 通用外键(ContentType)
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
class Comment(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
text = models.TextField()
post = Post.objects.get(id=1)
comment = Comment(content_object=post, text='Great post!')
comment.save()
六、性能优化策略
1. 查询优化方法
方法 | 作用描述 | 适用场景 |
---|
select_related | SQL JOIN 预取关联对象 | 外键、一对一关系 |
prefetch_related | 额外查询预取关联集合 | 多对多、一对多关系 |
only() | 限制加载字段 | 减少数据传输量 |
defer() | 排除指定字段 | 处理大文本字段 |
2. 索引优化原则
class Order(models.Model):
customer = models.ForeignKey(
User,
on_delete=models.CASCADE,
db_index=True
)
order_date = models.DateTimeField(
db_index=True
)
class Meta:
indexes = [
models.Index(fields=['customer', '-order_date']),
models.Index(
fields=['status'],
name='idx_active_orders',
condition=models.Q(status='active')
)
]
七、最佳实践指南
1. 命名规范
- 外键字段:使用
related_model_name + _id
格式(如 author_id
) - 反向关系:明确设置
related_name
(避免默认的 model_set
) - 中间模型:采用
Through
后缀(如 MembershipThrough
)
2. 版本控制
python manage.py makemigrations
python manage.py sqlmigrate app_name 0001
python manage.py migrate
3. 调试技巧
queryset = Article.objects.filter(tags__name='django')
print(queryset.query)
INSTALLED_APPS = [
...
'debug_toolbar',
]
MIDDLEWARE = [
'debug_toolbar.middleware.DebugToolbarMiddleware',
...
]
八、常见问题解决方案
1. 循环依赖问题
class Department(models.Model):
manager = models.ForeignKey(
'Employee',
on_delete=models.SET_NULL,
null=True
)
class Employee(models.Model):
department = models.ForeignKey(Department, on_delete=models.CASCADE)
class Department(models.Model):
pass
class Employee(models.Model):
department = models.ForeignKey(Department, on_delete=models.CASCADE)
Department.add_to_class(
'manager',
models.ForeignKey(
Employee,
on_delete=models.SET_NULL,
null=True
)
)
2. 数据迁移策略
from django.db import migrations
def transfer_old_data(apps, schema_editor):
OldModel = apps.get_model('app', 'OldModel')
NewModel = apps.get_model('app', 'NewModel')
for old in OldModel.objects.all():
NewModel.objects.create(
field1=old.field1,
related_field=old.related_field
)
class Migration(migrations.Migration):
dependencies = [...]
operations = [
migrations.RunPython(transfer_old_data),
]