一、核心特点
1. 中间表机制
自动隐式表 :Django 自动创建 <model1>_<model2>
中间表显式控制 :通过 through
参数自定义中间模型双向对称性 :默认对称关系(可设置 symmetrical=False
关闭)
2. 查询特性
article. tags. all ( )
tag. article_set. all ( )
Article. objects. filter ( tags__name= 'Python' )
Tag. objects. filter ( article__title__icontains= 'django' )
3. 性能优化点
prefetch_related
预加载关联集合中间表联合索引优化 批量操作减少查询次数
二、应用场景
1. 标签系统
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 )
slug = models. SlugField( unique= True )
2. 社交关系
class User ( models. Model) :
followers = models. ManyToManyField(
'self' ,
symmetrical= False ,
through= 'FollowRelationship' ,
related_name= 'following'
)
class FollowRelationship ( models. Model) :
from_user = models. ForeignKey( User, related_name= 'following_set' , on_delete= models. CASCADE)
to_user = models. ForeignKey( User, related_name= 'follower_set' , on_delete= models. CASCADE)
created_at = models. DateTimeField( auto_now_add= True )
class Meta :
unique_together = [ ( 'from_user' , 'to_user' ) ]
3. 权限系统
class Group ( models. Model) :
name = models. CharField( max_length= 80 , unique= True )
permissions = models. ManyToManyField(
'Permission' ,
through= 'GroupPermission' ,
related_name= 'groups'
)
class Permission ( models. Model) :
codename = models. CharField( max_length= 100 )
class GroupPermission ( models. Model) :
group = models. ForeignKey( Group, on_delete= models. CASCADE)
permission = models. ForeignKey( Permission, on_delete= models. CASCADE)
grant_date = models. DateField( )
三、ManyToManyField 参数精解
核心参数配置
models. ManyToManyField(
to= 'Tag' ,
through= 'ArticleTag' ,
related_name= 'tagged_articles' ,
related_query_name= 'tag' ,
symmetrical= False ,
through_fields= ( 'article' , 'tag' ) ,
limit_choices_to= { 'is_published' : True } ,
db_table= 'custom_m2m_table' ,
db_constraint= False ,
)
删除策略对照表
操作 影响范围 删除主对象 中间表记录自动删除 删除关联对象 中间表记录自动删除 清空关联关系 使用 clear()
方法删除中间表记录
四、CRUD 操作大全
1. 创建关联
article = Article. objects. create( title= "Django高级技巧" )
python_tag = Tag. objects. create( name= "Python" )
article. tags. add( python_tag)
tags = [ Tag( name= "Web" ) , Tag( name= "后端" ) ]
Tag. objects. bulk_create( tags)
article. tags. add( * tags)
relationship = FollowRelationship. objects. create(
from_user= user1,
to_user= user2
)
2. 查询操作
python_articles = Article. objects. filter ( tags__name= 'Python' )
from django. db. models import Count
tags = Tag. objects. annotate(
article_count= Count( 'articles' )
) . filter ( article_count__gt= 10 )
latest_follows = User. objects. filter (
following_set__created_at__gte= '2023-01-01'
)
Article. objects. filter (
tags__in= Tag. objects. filter (
articles__author__username= 'admin'
)
)
3. 更新操作
article. tags. set ( [ tag1, tag2, tag3] )
article. tags. add( tag4, through_defaults= { 'weight' : 0.8 } )
FollowRelationship. objects. filter (
from_user= user1
) . update( created_at= timezone. now( ) )
4. 删除操作
article. tags. remove( python_tag)
user. followers. clear( )
FollowRelationship. objects. filter (
created_at__lt= '2020-01-01'
) . delete( )
五、查询优化策略
1. 预加载技术
articles = Article. objects. prefetch_related( 'tags' )
users = User. objects. prefetch_related(
Prefetch( 'following_set' , queryset= FollowRelationship. objects. select_related( 'to_user' ) )
)
tags_prefetch = Prefetch(
'tags' ,
queryset= Tag. objects. only( 'name' )
)
Article. objects. prefetch_related( tags_prefetch)
2. 索引优化方案
class ArticleTag ( models. Model) :
article = models. ForeignKey( Article, on_delete= models. CASCADE)
tag = models. ForeignKey( Tag, on_delete= models. CASCADE)
weight = models. FloatField( default= 1.0 )
class Meta :
indexes = [
models. Index( fields= [ 'article' , 'tag' ] ) ,
models. Index( fields= [ '-weight' , 'tag' ] ) ,
models. Index( fields= [ 'tag' , 'article' ] , name= 'reverse_idx' )
]
六、高级应用场景
1. 复合关联模型
class Student ( models. Model) :
name = models. CharField( max_length= 100 )
class Course ( models. Model) :
title = models. CharField( max_length= 200 )
participants = 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)
semester = models. CharField( max_length= 20 )
grade = models. CharField( max_length= 2 )
class Meta :
unique_together = [ ( 'student' , 'course' , 'semester' ) ]
2. 自关联网络
class Person ( models. Model) :
name = models. CharField( max_length= 100 )
friends = models. ManyToManyField(
'self' ,
through= 'Friendship' ,
symmetrical= False
)
class Friendship ( models. Model) :
from_person = models. ForeignKey(
Person,
related_name= 'from_friendships' ,
on_delete= models. CASCADE
)
to_person = models. ForeignKey(
Person,
related_name= 'to_friendships' ,
on_delete= models. CASCADE
)
intimacy = models. IntegerField( default= 50 )
class Meta :
constraints = [
models. UniqueConstraint(
fields= [ 'from_person' , 'to_person' ] ,
name= 'unique_friendship'
)
]
3. 动态过滤关联
class ActiveTagManager ( models. Manager) :
def get_queryset ( self) :
return super ( ) . get_queryset( ) . filter ( is_active= True )
class Article ( models. Model) :
title = models. CharField( max_length= 200 )
all_tags = models. ManyToManyField( Tag)
active_tags = models. ManyToManyField(
Tag,
through= 'ActiveTag' ,
related_name= 'active_articles'
)
class ActiveTag ( models. Model) :
article = models. ForeignKey( Article, on_delete= models. CASCADE)
tag = models. ForeignKey( Tag, on_delete= models. CASCADE)
is_active = models. BooleanField( default= True )
objects = ActiveTagManager( )
七、常见问题解决方案
1. 性能优化方案
def bulk_add_tags ( article_ids, tag_ids) :
m2m_relations = [
Article. tags. through(
article_id= article_id,
tag_id= tag_id
)
for article_id in article_ids
for tag_id in tag_ids
]
Article. tags. through. objects. bulk_create( m2m_relations)
for tag in Tag. objects. iterator( ) :
process_tag( tag)
2. 循环依赖处理
class ModelA ( models. Model) :
related_bs = models. ManyToManyField( 'ModelB' )
class ModelB ( models. Model) :
related_as = models. ManyToManyField( ModelA)
ModelA. add_to_class(
'new_relation' ,
models. ManyToManyField( 'ModelC' )
)
3. 数据迁移策略
def migrate_old_relationships ( apps, schema_editor) :
OldRelation = apps. get_model( 'old_app' , 'OldM2M' )
NewRelation = apps. get_model( 'new_app' , 'NewM2M' )
batch = [ ]
for old in OldRelation. objects. iterator( ) :
batch. append( NewRelation(
source_id= old. source_id,
target_id= old. target_id,
extra_field= old. legacy_data
) )
if len ( batch) >= 1000 :
NewRelation. objects. bulk_create( batch)
batch = [ ]
if batch:
NewRelation. objects. bulk_create( batch)
class Migration ( migrations. Migration) :
dependencies = [ . . . ]
operations = [ migrations. RunPython( migrate_old_relationships) ]