【问题标题】:Django customization of loading a foreign keyDjango 自定义加载外键
【发布时间】:2021-04-20 04:59:30
【问题描述】:

假设我有三个相关的类,例如:

class A:
  ...

class B:
  ...
  a = models.ForeignKey(A, ...)

class C:
  ...
  b = models.ForeignKey(B, ...)

我想自定义访问C 对象上的b 以立即加载a。这意味着每当我打电话给c.b 一个select_related 查询电话a 。还有其他场景我需要能够使用prefetch_related

我正在使用django 3.1.1

【问题讨论】:

  • 您可以尝试使用自定义管理器并通过设置_base_manager 将其设置为基本管理器。见Base managers (Django docs)。 (基本管理器是用于访问相关对象的管理器)
  • 同一个类在不同外键中使用时是否可以定义不同的base_manager
  • 一个模型只有一个基地经理,你上面的意思是什么?你的意思是你想根据使用的 fk 在不同的模型上执行select_related 吗?无论如何,您的用例真的很奇怪。为什么不直接在需要时直接在C 上使用select_related
  • C 已在其他模型中使用过,我不想为所有模型定义 Manager 以加载 C 也强制所有模型加载 C 的参数。此外,C 中的 b 也不会一直使用。我理解您所说的简单调用 select_related 的意思,但我认为在我所说的情况下,这不是一个可行的解决方案,base_manager 可能是一个不错的选择。
  • 如果不是很清楚你会在这里为B 做一个基础经理。每当任何模型需要访问其相关的B 实例时,他们都会使用它的基本管理器。所以它不是C 模型的基础管理器。此外,如果您担心总是加载相关模型,则基本管理器和默认管理器可能会有所不同。

标签: django django-models django-orm


【解决方案1】:

如果你想重用一些带有外键的 select_related(),你可以这样做:

from django.db import models
from django.db.models.fields.related_descriptors import ForwardManyToOneDescriptor


class FullBForwardManyToOneDescriptor(ForwardManyToOneDescriptor):
    def get_object(self, instance):
        qs = self.get_queryset(instance=instance).select_related("a")
        # Assuming the database enforces foreign keys, this won't fail.
        return qs.get(self.field.get_reverse_related_filter(instance))


class FullBForeignKey(models.ForeignKey):
    forward_related_accessor_class = FullBForwardManyToOneDescriptor


class C:
  ...
  b = FullBForeignKey(B, ...)

class D:
  ...
  b = FullBForeignKey(B, ...)

class E:
  ...
  b = FullBForeignKey(B, ...)

如果您有许多 B 外键用例,您可以为每个用例创建继承 ForwardManyToOneDescriptor 和 ForeignKey 的其他类。

class F:
  ...
  b = SomeExtraBForeignKey(B, ...)

class G:
  ...
  b = SomeExtraBForeignKey(B, ...)  

【讨论】:

  • 据我了解,他/她想同时加载 b 和“a”内部关系,为什么不使用链选择相关,如 objects.select_related("b__a")?
  • @VeliEroglu 因为他/她似乎不想在任何地方复制 select_related("b__a") 。通过这种方式,您可以编写业务逻辑代码,而无需到处使用诸如 select_related() 或 prefetch_related() 之类的交错优化代码。
  • 使用forward_related_accessor_class是处理不同相关类中模型加载行为不同的场景的便捷方式。
猜你喜欢
  • 2020-01-10
  • 1970-01-01
  • 2019-06-06
  • 1970-01-01
  • 2016-08-22
  • 1970-01-01
  • 2011-11-18
  • 2020-05-02
  • 2010-11-12
相关资源
最近更新 更多