如何避免在Django中导入时数据库访问? [英] How to avoid import-time database access in Django?

查看:34
本文介绍了如何避免在Django中导入时数据库访问?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的Django应用程序针对我存储在 Category 模型中的事物具有许多类别.我经常在代码中引用这些内容,因此,我发现使用一个带有对这些类别和类别的引用(常量")的模块很有用,因此错别字很快就会失败.这也提供了缓存的好处.最后,它是实际模型,因此具有所有相关功能.看起来像这样:

My Django app has a number of categories for things which I store in a Category model. I reference these frequently in code, and so I've found it useful to have a module with references ("constants") to these categories and groups of them, so typos will fail fast. This also provides the benefit of caching. And finally, it's the actual model so it has all the related functionality. It looks something like this:

def load_category(name):
  return Category.objects.get(name=name)

DOGS = load_category("dogs")
CATS = load_category("cats")

但是,这会导致导入时数据库访问并引起各种问题.在添加具有此类参考的新类别之后,必须运行数据迁移,然后 ./manage.py 才能运行.我只是在切换到使用Django的测试框架时遇到了一个新问题,那就是这些负载是从默认(例如dev或prod)数据库加载的,而不是

However, this results in import-time database access and causes various issues. After adding a new category with a reference like this, I must run a data migration before ./manage.py will function. I just hit a new problem while switching to using Django's test framework, which is that these load from the default (e.g., dev or prod) database rather than the test one as explicitly mentioned in this warning.

如果您的代码尝试在其模块处于访问状态时访问数据库编译,这将在建立测试数据库之前发生潜在的意外结果.例如,如果您有一个数据库在模块级代码中查询,并且存在真实的数据库,生产数据可能会污染您的测试.有这样的导入时间是一个坏主意无论如何,您的代码中的数据库查询-重写您的代码,以便它不这样做.

If your code attempts to access the database when its modules are compiled, this will occur before the test database is set up, with potentially unexpected results. For example, if you have a database query in module-level code and a real database exists, production data could pollute your tests. It is a bad idea to have such import-time database queries in your code anyway - rewrite your code so that it doesn’t do this.

在避免导入时数据库访问的同时获得这些引用的好处的最佳模式是什么?

一种可能的解决方案是代理模式,该模式返回伪类别,该伪类别转发模型的所有功能,但在必要时才访问数据库.我想看看其他人如何通过这种方法或其他解决方案解决了这个问题.

One possible solution is a proxy pattern which returns a pseudo-Category which forwards all the model's functionality but does not access the database until it's necessary. I'd like to see how others have solved this problem with this approach or another solution.

(相关但不同的问题: Django测试.在运行测试时从生产数据库中查找数据吗?)

(Related but different question: Django test. Finding data from your production database when running tests?)

最终方法

@ kevin-christopher-henry的方法对我来说效果很好.但是,除了修复这些声明的引用外,我还必须延迟其他代码对这些引用的访问.在这里,我发现两种方法很有帮助.

The approach by @kevin-christopher-henry's worked well for me. However, in addition to fixing these declared references, I also had to delay access to the references from other code. Here I found two approaches helpful.

首先,我发现了 Python惰性对象代理.这个简单的对象以工厂函数作为输入,它被懒惰地执行以产生包装的对象.

First, I discovered Python Lazy Object Proxy. This simple object takes a factory function as input, which is lazily executed to produce the wrapped object.

MAP_OF_THINGS = Proxy(lambda: {
        DOG: ...
        CAT: ...
})

完成同一件事的一种类似方法是将代码推送到装饰有 memoize 的工厂函数中,因此它们只能执行一次.

A similar way of accomplishing the same thing was pushing code into factory functions decorated with memoize so they'd only be executed once.

注意:我最初尝试使用上面的Proxy对象作为我对模型对象的延迟访问问题的直接解决方案.但是,尽管非常很好,但是在查询和过滤这些对象时我还是得到了:

NOTE: I initially tried to use the Proxy object above as a direct solution to my problem of lazy access to model objects. However, despite being very good imitations, when querying and filtering on these objects I got:

TypeError: 'Category' object is not callable

果然, Proxy callable 返回了 True (即使文档说这不能保证其可调用).似乎Django查询太聪明了,势必会发现与电话模型不兼容的内容.

Sure enough, Proxy returns True for callable (even though docs say this doesn't guarantee it's callable). It seems that Django queries are just too smart and bound to find something incompatible with a phoney model.

对于您的应用程序来说, Proxy 可能就足够了.

For your application, Proxy might be good enough.

推荐答案

我本人也遇到过同样的问题,并且同意在这里拥有一些最佳实践会很棒.

I've run into the same issue myself, and agree that it would be great to have some best practices here.

我最终得到了一种基于描述符协议的方法:

I ended up with an approach based on the descriptor protocol:

class LazyInstance:
    def __init__(self, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs
        self.instance = None

    def __get__(self, obj, cls):
        if self.instance is None:
            self.instance, _ = cls.objects.get_or_create(*self.args, **self.kwargs)

        return self.instance

然后在我的模型类中,我有一些特殊的对象:

Then in my model classes I have some special objects:

class Category(models.Model):
    name = models.CharField()

    DOGS = LazyInstance(name="dogs")
    CATS = LazyInstance(name="cats")

因此在导入时什么也没有发生.第一次访问特殊对象时,将查找(并在必要时创建)相关实例并进行缓存.

So nothing happens at import time. The first time the special object is accessed, the relevant instance is looked up (and created, if necessary) and cached.

这篇关于如何避免在Django中导入时数据库访问?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆