Django多模型跨表查询求助:获取产品套餐关联价格
问题:获取套餐内产品的关联价格数据(Django ORM)
开发活动销售系统,events、products、price模型功能正常。当前bundle(产品套餐)模型关联多个产品,已获取当前查看产品对应的套餐列表,需通过Django ORM实现跨模型查询,获取套餐内产品的关联价格数据。
模型代码
from events.models import events import uuid import datetime class products(models.Model): id = models.UUIDField(primary_key=True, unique=True, default=uuid.uuid4, editable=False, verbose_name="ProductID") name = models.CharField(max_length=255, verbose_name="Product Name") linkedtoevent = models.ForeignKey(to=events, on_delete=models.CASCADE, verbose_name="Linked Event ID") def __str__(self): return self.name class price(models.Model): id = models.UUIDField(primary_key=True, unique=True, default=uuid.uuid4, editable=False, verbose_name="PriceRecordID") productid = models.ForeignKey(to=products, on_delete=models.CASCADE) price = models.FloatField(verbose_name="price") date_from = models.DateField(verbose_name="Date From") date_to = models.DateField(verbose_name="Date To") @property def status(self): today = datetime.date.today() # 注:原逻辑仅判断当天等于date_from或date_to,建议改为date_from<=today<=date_to if today in (self.date_to, self.date_from): return True else: return False class bundle(models.Model): id = models.UUIDField(primary_key=True, unique=True, default=uuid.uuid4, editable=False) name = models.CharField(max_length=255, verbose_name="Product Name") productsinbunble = models.ManyToManyField(products) date_from = models.DateField(verbose_name="Date To") # 注:verbose_name与字段名反向,建议修正 date_to = models.DateField(verbose_name="Date From") # 注:同上 discount = models.FloatField(verbose_name="Discount") def __str__(self): return self.name
解决方案及解释
1. 获取套餐+关联产品+所有价格记录
使用prefetch_related处理多对多关系,搭配Prefetch自定义关联查询:
from django.db.models import Prefetch # 假设current_product是当前查看的产品实例 bundles = bundle.objects.filter(productsinbunble=current_product).prefetch_related( Prefetch( 'productsinbunble__price_set', queryset=price.objects.all(), to_attr='all_prices' ) ) # 遍历使用示例 for bundle_obj in bundles: print(f"套餐:{bundle_obj.name}") for product in bundle_obj.productsinbunble.all(): print(f"产品:{product.name}") for price_obj in product.all_prices: print(f"价格:{price_obj.price},有效期:{price_obj.date_from}至{price_obj.date_to}")
2. 获取套餐+关联产品+当前有效价格
如果仅需要当前生效的价格(建议将status逻辑修正为date_from<=当天<=date_to),直接在Prefetch中过滤:
from django.db.models import Prefetch import datetime today = datetime.date.today() bundles = bundle.objects.filter(productsinbunble=current_product).prefetch_related( Prefetch( 'productsinbunble__price_set', queryset=price.objects.filter(date_from__lte=today, date_to__gte=today), to_attr='active_prices' ) ) # 遍历使用示例 for bundle_obj in bundles: print(f"套餐:{bundle_obj.name}") for product in bundle_obj.productsinbunble.all(): print(f"产品:{product.name}") for price_obj in product.active_prices: print(f"当前有效价格:{price_obj.price}")
3. 从产品反向查询套餐及关联价格
若已持有当前产品实例,可通过反向关联直接查询套餐:
from django.db.models import Prefetch import datetime today = datetime.date.today() current_product = products.objects.get(id="你的产品ID") # 反向关联名称默认是`bundle_set`(因未设置related_name) bundles = current_product.bundle_set.all().prefetch_related( Prefetch( 'productsinbunble__price_set', queryset=price.objects.filter(date_from__lte=today, date_to__gte=today), to_attr='active_prices' ) )
关键要点
- 多对多反向关联:products模型未给bundle的
productsinbunble字段设置related_name,Django默认生成bundle_set作为反向关联名称。 - 避免N+1查询:
prefetch_related用于处理多对多、反向外键这类跨表关联,一次性预加载关联数据,替代多次查询。 - 自定义关联查询:
Prefetch对象允许指定过滤后的查询集,还能通过to_attr设置别名,方便后续调用。 - 状态过滤优化:因
status是模型属性,无法直接用于ORM过滤,需直接用日期条件date_from__lte=today, date_to__gte=today实现相同逻辑(原status逻辑仅判断当天等于起止日期,存在逻辑漏洞,建议修正)。
内容的提问来源于stack exchange,提问作者adamt
相关产品推荐
相关产品推荐

