【发布时间】:2017-09-05 16:08:01
【问题描述】:
我的 Django 应用程序有许多类别,用于存储在 Category 模型中的东西。我在代码中经常引用这些,所以我发现有一个模块对这些类别和它们的组有引用(“常量”)很有用,所以拼写错误会很快失败。这也提供了缓存的好处。最后,它是实际模型,因此它具有所有相关功能。它看起来像这样:
def load_category(name):
return Category.objects.get(name=name)
DOGS = load_category("dogs")
CATS = load_category("cats")
但是,这会导致导入时数据库访问并导致各种问题。在添加具有这样引用的新类别后,我必须运行数据迁移,./manage.py 才能起作用。我在切换到使用 Django 的测试框架时遇到了一个新问题,即这些从默认(例如 dev 或 prod)数据库加载,而不是 this warning 中明确提到的测试数据库。
如果您的代码尝试访问数据库时,它的模块 编译,这将在设置测试数据库之前发生,与 潜在的意想不到的结果。例如,如果您有一个数据库 在模块级代码中查询并且存在真实数据库,生产数据 可能会污染您的测试。有这样的导入时间是个坏主意 无论如何,您的代码中的数据库查询 - 重写您的代码,以便它 不这样做。
在避免导入时数据库访问的同时获得这些引用的好处的最佳模式是什么?
一种可能的解决方案是一种代理模式,它返回一个伪类别,它转发所有模型的功能,但在必要之前不访问数据库。我想看看其他人是如何用这种方法或其他解决方案解决这个问题的。
(相关但不同的问题:Django test. Finding data from your production database when running tests?)
最终方法
@kevin-christopher-henry 的方法对我来说效果很好。但是,除了修复这些声明的引用之外,我还不得不延迟从其他代码访问引用。在这里,我发现两种方法很有帮助。
首先,我发现了Python Lazy Object Proxy。这个简单的对象将工厂函数作为输入,延迟执行以生成包装对象。
MAP_OF_THINGS = Proxy(lambda: {
DOG: ...
CAT: ...
})
完成同样事情的类似方法是将代码推送到用memoize 装饰的工厂函数中,这样它们只会被执行一次。
注意:我最初尝试使用上面的代理对象作为我对模型对象的延迟访问问题的直接解决方案。然而,尽管是 非常 很好的仿制品,但在对这些对象进行查询和过滤时,我得到了:
TypeError: 'Category' object is not callable
果然,Proxy 为callable 返回True(尽管文档说这并不能保证它是可调用的)。似乎 Django 查询太聪明了,必然会找到与虚假模型不兼容的东西。
对于您的应用程序,Proxy 可能就足够了。
【问题讨论】: