Django通过不同的相关对象注释QuerySet中的几个相同对象
Posted
技术标签:
【中文标题】Django通过不同的相关对象注释QuerySet中的几个相同对象【英文标题】:Django annotate several same objects in QuerySet by different related object 【发布时间】:2019-06-19 23:49:39 【问题描述】:我明白了:
# models
class Building(models.Model):
...
class Flat(models.Model):
building = models.ForeignKey(Building)
class Profile(models.Model):
flats = models.ManyToManyField(Flat)
# logic
building = Building.objects.create()
flat_1 = Flat.objects.create(building=building)
flat_2 = Flat.objects.create(building=building)
profile = Profile.objects.create()
profile.flats.add(flat_1)
profile.flats.add(flat_2)
profiles = Profile.objects.filter(flats__building=building)
我获得了profiles
2 个相同的个人资料。我如何用不同的flat
注释它们中的每一个:profiles.first().flat == flat_1
和profiles.last().flat == flat_2
?
也许是Subquery()
,但如何?
UPD 我在某些 DRF 列表视图中需要这个。 JSON 格式的输出必须类似于:
[
"profile_id": 1,
"flat_id": 2
,
"profile_id": 1,
"flat_id": 3
]
【问题讨论】:
不完全确定您要达到的目标,但是如果您将through
-model (docs.djangoproject.com/en/2.1/ref/models/fields/…) 添加到您的ManyToManyField
,这样的事情会变得更容易,然后您可以对此进行查询关系也是...
@BernhardVallant 是的,这是一个选项。谢谢!
【参考方案1】:
要获得该输出,您可以这样做:
data = Profile.objects.all().values('flats', 'id')
return Response(data=data)
在您的 DRF 视图中。
【讨论】:
:)。我有 350 行代码的视图类,带有过滤器、排序等,我不能像.values()
那样简单。中间有很多业务逻辑。我真的需要像 .annotate()
这样的东西来为查询集中的每个对象添加正确的平面或类似的字段。
我认为这是一个正确的答案。无论您的查询集有多复杂,您始终可以将.values('flats', 'id')
放在最后。 filter
和 values
的顺序可以互换
我认为为了获得像 OP 所要求的 json,您应该过滤单位,而不是获取所有配置文件对象,因为关系是 many2many。更干净/优雅/直接的方法需要改造。也许这就是 OP 应该考虑的......【参考方案2】:
您不必分析实例...
我最后为您的确切需求编写了代码,但首先写了一些可能感兴趣的东西。
在您的代码示例中,您只创建了一个配置文件,我确定您没有获得 2 个相等的配置文件实例,而只有一个。
问题是,如果您有一个只有一个条目的 QuerySet,那么:
profiles.first() == profiles.last() # True
因为profile.first()
和profiles.last()
是同一个实例。
您应该尝试创建 2 个 Profile 实例:
building = Building.objects.create()
flat_1 = Flat.objects.create(building=building)
flat_2 = Flat.objects.create(building=building)
profile_1 = Profile.objects.create() # You coud/should use bulk_create here.
profile_2 = Profile.objects.create()
profile_1.flats.add(flat_1)
profile_2.flats.add(flat_2)
然后
profiles = Profile.objects.filter(flats__building=building)
将返回两个不同的配置文件对象。
另一方面,获取你想要的 JSON ...
按照示例,您发布,按配置文件过滤公寓并获取值(如果您有多个配置文件,这也适用)。
Flat.objects.filter(profile=profile_1).values('profile__id', 'id')
这将返回类似(“id”代表公寓id
s):
[
"profile__id": 1,
"id": 1
,
"profile__id": 1,
"id": 3
]
如果您不按个人资料过滤(并且您有多个),您可能会得到如下信息:
[
"profile__id": 1,
"id": 1
,
"profile__id": 2,
"id": 3
,
"profile__id": 2,
"id": 4
,
...
]
注释以获得您想要的 EXACT json:
过滤如上图注解,得到想要的值:
Flat.objects.filter(profile=profile_1).annotate(
flat_id=F('id')
).annotate(
profile_id=F('profile__id')
).values(
'profile_id', 'flat_id'
)
会给出你想要的:
[
"profile_id": 1,
"flat_id": 2
,
"profile_id": 1,
"flat_id": 3
]
【讨论】:
【参考方案3】:您可以使用正确的序列化程序和正确的注释来做到这一点:
序列化器:
class FlatSerializer(serializers.ModelSerializer):
class Meta:
model = Flat
fields = ('flat_id', 'building_id')
flat_id = serializers.CharField(read_only=True)
然后我会简单地查询 Flats
而不是配置文件并序列化:
flats = Flat.objects \
.annotate(flat_id=F('id')) \
.filter(building=building)
serialized = FlatSerializer(flats, many=True)
print(serialized.data) # [ flat_id: 1, building_id: 1 , flat_id: 2, building_id: 1 ]
让我知道这是否适合你
【讨论】:
tgdn 感谢您的回答。我的问题是在profiles
查询集中制作双打,并在配置文件和平面之间用m2m 的不同平面注释每个双打。你的代码是关于别的东西的:)。以上是关于Django通过不同的相关对象注释QuerySet中的几个相同对象的主要内容,如果未能解决你的问题,请参考以下文章
如何使用相同的值但使用现有字段之一的不同数据类型来注释 Django Queryset?
通过特定的 ManyToMany 对象订购 Django QuerySet
Django:将带有相关对象的 QuerySet 转换为 JSON
从 Django QuerySet 中获取所有相关的多对多对象