Django多对多关系下如何仅用筛选按类别和广告类型查询广告?
不用手动循环!Django实现按广告类型+类别检索广告的高效方案
当然可以做到!完全不用你手动写循环拼字典,Django的ORM和序列化工具就能帮你直接生成想要的结构化数据,而且广告对象也能完整包含进去,下面给你两种靠谱的实现方式:
方式一:用Django REST Framework序列化器(推荐,灵活且易维护)
这种方式最直观,能直接输出你想要的格式,还能自定义返回的字段:
第一步:定义序列化器
先在你的项目里创建一个serializers.py文件,写入以下代码:
from rest_framework import serializers from .models import Advertisement_Type, Advertisement, Category class AdvertisementSerializer(serializers.ModelSerializer): # 这里可以自定义广告对象返回的字段,比如只返回title和id就写fields = ['id', 'title'] class Meta: model = Advertisement fields = '__all__' class CategorySerializer(serializers.ModelSerializer): class Meta: model = Category fields = ['name'] class AdvertisementTypeSerializer(serializers.ModelSerializer): # 把多对多的category转成名字列表 category = serializers.SerializerMethodField() # 关联的广告集合,source指定关联的反向查询名 advertisement = AdvertisementSerializer(many=True, source='advertisement_set') def get_category(self, obj): # 直接返回类别名称的列表,JSON不支持集合格式,用列表是前后端交互的标准方式 return [cat.name for cat in obj.ad_category.all()] class Meta: model = Advertisement_Type fields = ['name', 'category', 'advertisement']
第二步:查询并序列化数据
在视图或者业务逻辑里,用ORM预取关联数据(避免N+1查询问题),然后序列化:
from django.db.models import Prefetch from .models import Advertisement_Type from .serializers import AdvertisementTypeSerializer # 预取关联的类别和广告,大幅提升查询效率 ad_types = Advertisement_Type.objects.prefetch_related( 'ad_category', Prefetch('advertisement_set', queryset=Advertisement.objects.all()) ).all() # 序列化得到目标格式的数据 result = AdvertisementTypeSerializer(ad_types, many=True).data
这样result就是你想要的结构化数据,每个广告类型下包含名称、类别列表和完整的广告对象集合。
方式二:用ORM聚合(适合简单场景,需PostgreSQL支持)
如果你的项目用的是PostgreSQL数据库,可以用ArrayAgg直接在数据库层面聚合数据,不需要序列化器:
from django.db.models import ArrayAgg from .models import Advertisement_Type ad_types = Advertisement_Type.objects.annotate( # 聚合得到类别名称的数组 category=ArrayAgg('ad_category__name', distinct=True), # 如果只需要广告的标题等字段,可以这样聚合;要完整对象的话还是推荐序列化器 advertisement_titles=ArrayAgg('advertisement_set__title', distinct=True) ).values('name', 'category', 'advertisement_titles')
这个方法直接从数据库拿到聚合后的结果,但缺点是没法直接返回完整的广告对象,适合只需要部分字段的场景。
小提示
- 你示例里的集合格式(
{'Health', 'Engineering'})在JSON中不被支持,所以实际返回用数组(列表)是行业通用的标准做法。 - 一定要用
prefetch_related优化查询,否则会触发大量重复数据库请求,拖慢性能。
内容的提问来源于stack exchange,提问作者Paolo
相关产品推荐
相关产品推荐

