通过装饰器指定模型元数据(django)

3 投票
1 回答
1392 浏览
提问于 2025-04-17 00:10

问题是:我想在数据库层面上使用Postgres的模式(Schemas)来分开我Django应用中不同部分的表。

旁白

这一部分可以跳过,但我觉得加点背景信息会有帮助。我的应用是基于一个已有的数据数据库(存储在public模式中),我非常不希望对它进行修改。因此,我想把“我的”数据放到一个单独的模式中(Django会被赋予读/写的权限),同时限制对public模式的访问为只读。我最开始尝试通过把我的数据分到一个单独的数据库里,并使用数据库路由来解决这个问题,但后来发现(如果我早一点看文档就好了)Django不支持跨数据库的依赖关系(这也算合理),而我的模型中有外键指向只读数据。

重点

对于Django缺乏模式支持的问题,有一个解决办法(你可以在这里了解更多),就是在模型的元信息中指定db_table属性,像这样:

class MyModel(models.Model):
    attribute1 = models.CharField()

     #Fool django into using the schema
    class Meta:
        db_table = 'schema_name\".\"table_name'

这很好,但我不想为我应用中的每一个模型都写这个 - 首先,这样看起来不够“Python风格”,而且我很可能在添加新模型时忘记写。

我的解决方案是以下这段代码:

def SchemaBasedModel(cls):
    class Meta:
        db_table = '%s\".\"%s' % (schema_name, cls.__name__)
    cls.Meta = Meta
    return cls

@SchemaBasedModel
class MyModel(models.Model):
    attribute1 = models.CharField()
    ...

当我运行python manage.py shell时,我得到了以下结果:

>>> from myapp import models
>>> myModel = models.MyModel
>>> myModel.Meta.db_table
'myschema"."mymodel'
>>> 

我心想:“看起来不错。”然后我运行了:python manage.py sqlall myapp。可惜,这个命令返回的还是原来的表名,也就是在我应用这个元信息之前的表名。当我回去手动添加元信息(也就是在所有模型中添加Meta内部类)时,结果就如我所期待的那样(新的表名)。

我希望有人能告诉我这里发生了什么?或者,更有用的是,怎么做才是“正确”的方式?我以为我在这里提到的装饰器模式可以解决这个问题,但显然行不通。我该如何快速且轻松地将这个元信息应用到我所有的模型中,而不必每次都手动输入?

编辑:也许我在提问时有点不清楚 - 我同样想知道“实际发生了什么”(也就是为什么事情没有按照我想的那样进行 - 我在这里误解了什么?)以及如何解决我的问题(希望能在模式层面上清晰地分开“我的”数据和遗留数据 - 但如果我必须把所有东西都放到public模式中,并按表管理权限,那也不是世界末日)。

第二次编辑:被接受的回答不一定告诉我我真正想知道的,但它可能是解决实际问题的正确方案。简短的回答是:不要这样做。

1 个回答

2

我其实不想为我应用中的每一个模型都写这个 - 首先,这看起来不太像是Python的风格,

这不对。有些东西必须明确写出来。“明确总比隐含好”。

而且我很可能会忘记在添加新模型时需要写这些

这也是不对的。

你不会“忘记”的。

总之:不要在这种事情上搞混。只需在必要的地方明确写出这两行代码。

你没有那么多表。

你不会忘记的。

另外,确保使用数据库权限。只在你的“旧”表(你不想写入的表)上授予SELECT权限。这样你就不能往里面写数据了。

撰写回答