【问题标题】:How can I use prefetch_related in self related model如何在自相关模型中使用 prefetch_related
【发布时间】:2018-08-06 02:33:48
【问题描述】:

我有 Menu 模型,它的 ForeignKey 命名为 parent 与自身相关。

如果 parent 为 None,则表示此菜单是父菜单,如果显示另一个 Menu 对象,则表示它是其父级的子菜单(多对一关系)

这是我的问题,我想使用 prefetch_related 获取所有菜单及其子菜单,我该怎么做?

注意:我不想每次都让子菜单进入数据库 菜单

这是我的模型类

class Menu(models.Model):
    title = models.CharField(max_length=30)
    language = models.ForeignKey(Language)
    parent = models.ForeignKey("self", default=None, blank=True, null=True, related_name="submenus")

这是我的查询

pm2 = Menu.objects.filter(parent=None, language__code=language, menutype=menutype).prefetch_related("submenus").order_by("order")
for p in pm2:
    print(p.title)
    print(p.submenus)

当我打印子菜单时,结果是app.Menu.None

【问题讨论】:

  • 您的模型根本没有提到submenus,所以我希望.prefetch_related("submenus") 给出一个错误。您的子菜单会有自己的子菜单,还是您的菜单只有两个级别?如果您有多个级别,那么您可能需要查看django-mptt
  • django-mptt 使用起来非常复杂。我建议改用物化路径(例如communities.bmc.com/docs/DOC-9902
  • 这个问题解决了吗?
  • @Bigbob556677 这根本不是问题。请查看my answer 了解原因。
  • queryset = Menu.objects.filter(parent=None, language__code=language, menutype=menutype).order_by("order").prefetch_related('submenus', 'submenus__submenus', 'submenus__submenus__submenus', “子菜单__子菜单__子菜单__子菜单”)

标签: django django-models django-orm


【解决方案1】:

例如,你有 4Level self 你需要这样的代码:

queryset = Menu.objects.filter(parent=None, language__code=language, menutype=menutype).order_by("order").prefetch_related('submenus', 'submenus__submenus', 'submenus__submenus__submenus', "submenus__submenus__submenus__submenus")

【讨论】:

  • 这看起来是个糟糕的主意。如果有 5 个级别呢?
【解决方案2】:

当我打印子菜单时,结果是app.Menu.None

发生这种情况是因为 OP 正在打印 print(p.submenus),这只是一个 RelatedManager。可以通过查看p.submenus的类型来验证:

# this is what it prints in django 1.11
>>> type(p.submenus)                                                                                                                                                                                                               
django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager.<locals>.RelatedManager

要从 m2m 关系访问查询集,正确的方法是使用.all()

print(p.submenus.all())

【讨论】:

  • 你回答了这个问题吗?我认为您发布了错误的问题
  • @Kevin 是的,我已经回答了这个问题。很抱歉,但我不理解你所说的 i think you post wrong question 的意思。
【解决方案3】:

由于您使用的是树(通过使用邻接列表),因此如果您有多个级别的父子关系,select_relatedprefetch_related 将无法正常工作。相反,您将需要使树具体化的东西。

https://pypi.org/project/django-closure-tree/ 提供了一个框架来构建 Postgres 视图来处理这种类型的结构,并为您提供一些工具来简化查询。

这背后的实际代码非常简单:https://schinckel.net/2016/01/27/django-trees-via-closure-view/

【讨论】:

    【解决方案4】:

    您不需要使用prefetch_related,因为它用于many-to-many 关系,您可以使用select_related

    所以你的查询是

    pm2 = Menu.objects.filter(parent=None, language__code=language, menutype=menutype).select_related("submenus").order_by("order")
    

    【讨论】:

    • 对迟到的反应感到抱歉。在这里我认为我需要使用 prefetch_related 因为我想获取子菜单,而子菜单是反向 ForeignKeys“父”字段。
    猜你喜欢
    • 2020-08-27
    • 2021-11-01
    • 2018-01-15
    • 1970-01-01
    • 2013-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-28
    相关资源
    最近更新 更多