Django 通过模型从 m2m 按字段注释查询集
Django annotate queryset by field from m2m through model
我有模型 BottleType 和 OrganisationBottleType。
我想通过 OrganisationBottleType.'is_accepted' 和 'points' 字段注释 BottleType 查询集。
OrganisationBottleType 模型:
class OrganisationBottleType(models.Model):
organisation = models.ForeignKey(
'Organisation',
related_name='organisation_bottle_types',
on_delete=models.CASCADE,
)
bottle_type = models.ForeignKey(
'machines.BottleType',
related_name='organisation_bottle_types',
on_delete=models.CASCADE,
)
is_accepted = models.BooleanField(...)
points = models.FloatField(...)
假设我有 organisation_id 和 BottleType 查询集,因此对于查询集中的每个对象,需要找到由 bottle_type 过滤的 OrganisationBottleType 和组织,并注释字段 'is_accepted' 和 'points' .
(当找到过滤后的 OrganisationBottleType qs 时,可以只从中获取第一个对象,因为假设两个字段:organization - bottle_type 是唯一的)
我的想法是在注释中使用子查询,但我做不对。
不胜感激!
如果我没理解错的话,对于你想要的每种瓶子类型:
- 找到它的点
- 检查是否被接受。
有两种方法可以解决您的问题:
- 在
.prefetch_related()
和Prefetch()
对象的帮助下,获取OrganisationBottleType
查询集并将OrganisationBottleType
对象匹配到python中BottleType
中的相应对象
- 在
.annotate()
和 Subquery()
的帮助下,使用来自 OrganisationBottleType
的相应对象注释 BottleType
查询集
两个选项如下所述:
1.将 .prefetch_related
与 Prefetch
对象
一起使用
鉴于:
queryset
- BottleType
查询集
organisation_id
- 所需组织的编号
解决方案:
queryset = queryset.prefetch_related(
Prefetch(
'organisation_bottle_types',
queryset= OrganisationBottleType.objects.filter(organisation_id=organisation_id)
)
)
之后您可以通过以下方式检索所需的数据:
for bottle_type in queryset:
if bottle_type.organisation_bottle_types.all():
related_object = bottle_type.organisation_bottle_types.all()[0]
is_accepted = related_object.is_accepted
points = related_object.points
else:
is_accepted = False
points = None
2。使用 SubQuery
鉴于:
queryset
- BottleType
查询集
organisation_id
- 所需组织的编号
解决方案:
organisation_bottle_types = OrganisationBottleType.objects.filter(organisation_id=organisation_id, bottle_type=OuterRef('id'))
queryset = queryset.annotate(
is_accepted=Subquery(organisation_bottle_types.values('is_accepted')[:1])
).annotate(
points=Subquery(organisation_bottle_types.values('points')[:1], output_field=BooleanField())
)
之后可以做:
for bottle_type in queryset:
is_accepted = bottle_type.is_accepted
points = bottle_type.points
简历:
就个人而言,我会选择第二个选项,因为它会在数据库级别而不是代码级别执行所有匹配逻辑。
第一个选项更好,当你需要匹配整个对象时,而不仅仅是几个字段(比如你问题中的 points
和 is_accepted
)
我有模型 BottleType 和 OrganisationBottleType。 我想通过 OrganisationBottleType.'is_accepted' 和 'points' 字段注释 BottleType 查询集。
OrganisationBottleType 模型:
class OrganisationBottleType(models.Model):
organisation = models.ForeignKey(
'Organisation',
related_name='organisation_bottle_types',
on_delete=models.CASCADE,
)
bottle_type = models.ForeignKey(
'machines.BottleType',
related_name='organisation_bottle_types',
on_delete=models.CASCADE,
)
is_accepted = models.BooleanField(...)
points = models.FloatField(...)
假设我有 organisation_id 和 BottleType 查询集,因此对于查询集中的每个对象,需要找到由 bottle_type 过滤的 OrganisationBottleType 和组织,并注释字段 'is_accepted' 和 'points' .
(当找到过滤后的 OrganisationBottleType qs 时,可以只从中获取第一个对象,因为假设两个字段:organization - bottle_type 是唯一的)
我的想法是在注释中使用子查询,但我做不对。
不胜感激!
如果我没理解错的话,对于你想要的每种瓶子类型:
- 找到它的点
- 检查是否被接受。
有两种方法可以解决您的问题:
- 在
.prefetch_related()
和Prefetch()
对象的帮助下,获取OrganisationBottleType
查询集并将OrganisationBottleType
对象匹配到python中BottleType
中的相应对象 - 在
.annotate()
和Subquery()
的帮助下,使用来自
OrganisationBottleType
的相应对象注释 BottleType
查询集
两个选项如下所述:
1.将 .prefetch_related
与 Prefetch
对象
鉴于:
queryset
-BottleType
查询集organisation_id
- 所需组织的编号
解决方案:
queryset = queryset.prefetch_related(
Prefetch(
'organisation_bottle_types',
queryset= OrganisationBottleType.objects.filter(organisation_id=organisation_id)
)
)
之后您可以通过以下方式检索所需的数据:
for bottle_type in queryset:
if bottle_type.organisation_bottle_types.all():
related_object = bottle_type.organisation_bottle_types.all()[0]
is_accepted = related_object.is_accepted
points = related_object.points
else:
is_accepted = False
points = None
2。使用 SubQuery
鉴于:
queryset
-BottleType
查询集organisation_id
- 所需组织的编号
解决方案:
organisation_bottle_types = OrganisationBottleType.objects.filter(organisation_id=organisation_id, bottle_type=OuterRef('id'))
queryset = queryset.annotate(
is_accepted=Subquery(organisation_bottle_types.values('is_accepted')[:1])
).annotate(
points=Subquery(organisation_bottle_types.values('points')[:1], output_field=BooleanField())
)
之后可以做:
for bottle_type in queryset:
is_accepted = bottle_type.is_accepted
points = bottle_type.points
简历:
就个人而言,我会选择第二个选项,因为它会在数据库级别而不是代码级别执行所有匹配逻辑。
第一个选项更好,当你需要匹配整个对象时,而不仅仅是几个字段(比如你问题中的 points
和 is_accepted
)