从接触数据库的 类 解耦 Django 信号

Decoupling Django signals from classes that touch the DB

我有一个发送信号的应用程序,我们称它为 signal_x。在另一个应用程序中,为 signal_x 定义了一个 @receiver,在它的 apps.py 中,ready() 函数是包含接收机进口。

@receiver 函数有一个问题。它依赖于一个对象,我们称之为 object_y,它的构造函数依赖于 DB。使用从数据库中提取的值,构建了一些重对象。这些 heavy_objects 必须在内存中,以便进行非常快速的计算,因此它们不能延迟加载,因为它们在第一次使用时会花费太长时间。 self.location_ids 也不能延迟加载,因为它被两个 compute_heavy_... 函数使用。

一切正常,直到我们删除数据库,重新创建它并 运行 manage.py makemigrations。现在 makemigrations 将失败,因为 Django 首先 运行s django.setup(),运行s 每个应用程序的 ready() 函数。当它通过导入到达 ready() 函数时,它将按预期导入文件并尝试创建 object_y。但是 object_y 需要尚不存在的模型,因此会出现错误。我怎么能优雅地solve/circumvent这个?

class Recommender:
    def __init__(self):
        self.location_ids = self.get_location_ids()
        self.heavy_object1 = self.compute_heavy_object1(location_ids)
        self.heavy_object2 = self.compute_heavy_object2(..., location_ids)

    def get_location_ids():
        locations = LocationNode.objects.filter(level=1, parent_id=1)
        return [location.id for location in locations]

... meantime in another file

object_y = Recommender()

@receiver(signal_x, dispatch_uid="update_state")
def recompute(sender, **kwargs):
    client_id = kwargs['client_id']
    # prepare some things
    heavy_recompute(object_y, client_id)

这是一个常见的用例。由于您没有提供那么多代码,因此很难给您一个非常准确的答案,但是,考虑一下:

  • AppConfig.ready() 函数中,您应该始终使用 self.get_model() to retrieve a reference to a model defined in another app. This will ensure the app registry is up-to-date if require_ready=True (the default). Outside a AppConfig method, you can use apps.get_model()
  • 不要在模块级别创建模型实例 (object_y = HeavyObject())。注意:我不知道 HeavyObject 是不是 Django 模型。但一般情况下,你可以默认声明object_y = None,稍后再初始化变量