在 Django 中,多对多(ManyToMany)关系是一种常见的数据关系模型,它允许一个模型实例与另一个模型的多个实例相关联。Django 通过中间表自动管理这种关系。
# 假设有模型 Article 和 Tag,多对多关系
articles = Article.objects.filter(tags__name='django')
# 从 Tag 查询关联的 Article
tags = Tag.objects.filter(article__title__contains='Django')
# 多个条件组合
articles = Article.objects.filter(
tags__name='django',
tags__category='web'
).distinct() # 使用distinct()避免重复
# 排除带有特定标签的文章
articles = Article.objects.exclude(tags__name='deprecated')
如果使用 through
定义了自定义中间模型:
# 模型定义
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
# 查询
groups = Group.objects.filter(
membership__date_joined__gte=date(2023,1,1)
)
原因:多对多关系可能导致同一主对象因匹配多个相关对象而重复出现。
解决方案:使用 distinct()
Article.objects.filter(tags__name='python').distinct()
解决方案:使用 Q 对象
from django.db.models import Q
# 查找标签为python或django的文章
Article.objects.filter(
Q(tags__name='python') | Q(tags__name='django')
).distinct()
优化方案:
select_related
和 prefetch_related
# 优化查询
Article.objects.filter(tags__name='django').prefetch_related('tags')
from django.db.models import Count
# 查找有超过3个标签的文章
Article.objects.annotate(tag_count=Count('tags')).filter(tag_count__gt=3)
# 查找有特定标签集合的文章
from django.db.models import Exists, OuterRef
django_tag = Tag.objects.filter(name='django')
articles = Article.objects.filter(
Exists(django_tag.filter(article=OuterRef('pk')))
)
# 自定义查找方法
class TaggedItemFilter(django_filters.FilterSet):
class Meta:
model = Article
fields = {
'tags__name': ['exact', 'contains'],
}
通过掌握这些多对多字段的过滤技巧,可以高效地处理Django中的复杂数据关系查询。