gpt4 book ai didi

django:预取 GenericForeignKey 的相关对象

转载 作者:行者123 更新时间:2023-12-03 14:59:28 26 4
gpt4 key购买 nike

假设我有一个模型 BoxGenericForeignKey指向 Apple实例或 Chocolate实例。 AppleChocolate ,反过来,拥有 Farm 的 ForeignKeys和 Factory , 分别。我想显示 Box 的列表es,为此我需要访问 FarmFactory .如何在尽可能少的数据库查询中做到这一点?

最小的说明性示例:

class Farm(Model):
...

class Apple(Model):
farm = ForeignKey(Farm)
...

class Factory(Model):
...

class Chocolate(Model):
factory = ForeignKey(Factory)
...

class Box(Model)
content_type = ForeignKey(ContentType)
object_id = PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
...

def __unicode__(self):
if self.content_type == ContentType.objects.get_for_model(Apple):
apple = self.content_object
return "Apple {} from Farm {}".format(apple, apple.farm)
elif self.content_type == ContentType.objects.get_for_model(Chocolate):
chocolate = self.content_object
return "Chocolate {} from Factory {}".format(chocolate, chocolate.factory)

这是我尝试过的几件事。在所有这些示例中, 是盒子的数量。查询计数假设 ContentType代表 AppleChocolate已经被缓存了,所以 get_for_model()电话不会打到数据库。

1)天真:
print [box for box in Box.objects.all()]
这是 1 (获取盒子)+ (每盒取苹果或巧克力)+ (获取每个 Apple 的 Farm 和每个 Chocolate 的 Factory)查询。

2) select_related在这里没有帮助,因为 Box.content_objectGenericForeignKey .

3) 从 django 1.4 开始, prefetch_related可以获取 GenericForeignKey s。
print [box for box in Box.objects.prefetch_related('content_object').all()]
这是 1 (获取盒子)+ 2 (为所有盒子获取苹果和巧克力)+ (获取每个 Apple 的 Farm 和每个 Chocolate 的 Factory)查询。

4) 显然 prefetch_related不够聪明,无法遵循 GenericForeignKeys 的 ForeignKeys。如果我尝试:
print [box for box in Box.objects.prefetch_related(
'content_object__farm',
'content_object__factory').all()]

它理所当然地提示 Chocolate对象没有 farm场,反之亦然。

5)我可以这样做:
apple_ctype = ContentType.objects.get_for_model(Apple)
chocolate_ctype = ContentType.objects.get_for_model(Chocolate)
boxes_with_apples = Box.objects.filter(content_type=apple_ctype).prefetch_related('content_object__farm')
boxes_with_chocolates = Box.objects.filter(content_type=chocolate_ctype).prefetch_related('content_object__factory')

这是 1 (获取盒子)+ 2 (为所有盒子获取苹果和巧克力)+ 2 (获取所有苹果的农场和所有巧克力的工厂)查询。缺点是我必须手动合并和排序两个查询集( boxes_with_applesboxes_with_chocolates )。在我的实际应用程序中,我在分页的 ModelAdmin 中显示这些框。如何在此处集成此解决方案并不明显。也许我可以编写一个自定义分页器来透明地进行缓存?

6) 我可以根据 this 拼凑一些东西这也做了 O(1) 查询。但是,如果可以避免的话,我宁愿不要弄乱内部结构( _content_object_cache )。

总结: 打印 Box 需要访问 GenericForeignKey 的 ForeignKey。如何在 O(1) 查询中打印 N 个框? (5) 是我能做的最好的,还是有更简单的解决方案?

加分: 您将如何重构此 DB 模式以使此类查询更容易?

最佳答案

您可以手动实现类似 prefetch_selected并使用 Django 的 select_related方法,这将在数据库查询中加入。

apple_ctype = ContentType.objects.get_for_model(Apple)
chocolate_ctype = ContentType.objects.get_for_model(Chocolate)
boxes = Box.objects.all()
content_objects = {}
# apples
content_objects[apple_ctype.id] = Apple.objects.select_related(
'farm').in_bulk(
[b.object_id for b in boxes if b.content_type == apple_ctype]
)
# chocolates
content_objects[chocolate_ctype.id] = Chocolate.objects.select_related(
'factory').in_bulk(
[b.object_id for b in boxes if b.content_type == chocolate_ctype]
)

这应该只进行 3 个查询( get_for_model 查询被省略)。 in_bulk 方法返回格式为 {id: model} 的字典。因此,要获取您的 content_object,您需要如下代码:
content_obj = content_objects[box.content_type_id][box.object_id]

但是我不确定这段代码是否会比你的 更快。 O(5) 解决方案,因为它需要对框查询集进行额外的迭代,并且它还生成一个带有 WHERE id IN (...) 的查询陈述。

但是,如果您仅按 Box 模型中的字段对框进行排序,则可以填写 content_objects分页后的dict。但是你需要通过 content_objects__unicode__不知何故。

How would you refactor this DB schema to make such queries easier?



我们有类似的结构。我们存储 content_objectBox , 而不是 object_idcontent_object我们使用 ForeignKey(Box)AppleChocolate .在 Box我们有一个 get_object返回 Apple 或 Chocolate 模型的方法。在这种情况下,我们可以使用 select_related ,但在我们的大多数用例中,我们按 content_type 过滤 Boxes。因此,我们遇到了与您的第 5 个选项相同的问题。但是当没有 prefetch_selected 时,我们在 Django 1.2 上开始了我们的项目。

如果您将农场/工厂重命名为某个通用名称,例如创建者,prefetch_related 会起作用吗?

关于您的 选项 6

我不能说任何反对填写 _content_object_cache .
如果您不喜欢处理内部问题,您可以填写自定义属性,然后使用
apple = getattr(self, 'my_custop_prop', None)
if apple is None:
apple = self.content_object

关于django:预取 GenericForeignKey 的相关对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12466945/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com