Django:用于管理 db_table 的抽象基础 class

Django: Abstract base class for managing db_table

我正在尝试构建我的第二个 django(并且 python 就此而言,我的第一个项目是 django 教程 :))项目。因为这应该是真实的东西,所以我想在进入项目的实质之前彻底并构建一个好的代码结构。

我有几个像这样的简单模型

class Task(models.Model):
    name = models.CharField(max_length=50, null=False)
    description = models.CharField(max_length=255, null=True)
    dueDate = models.DateTimeField()

我正在使用 PostgreSQL,我通过像这样定义每个模型的元 class 来设置我的模型以使用应用程序标签作为数据库模式

class Meta:
    managed = True
    db_table = 'app_name\".\"modelname'

这很好用。但是我必须对每个模型都这样做。

不过我想保持干燥。所以我现在想做的是有一个自动执行此操作的抽象基础 class 所以我尝试了以下方法:

class SchemaModel(models.Model):

    class Meta():
        abstract = True
        managed = True
        db_table = AppConfig.label+'\".\"'+self.__class__.lower()+'s'

(基础 class 然后当然被继承了,我从普通模型中取出了嵌套的 Meta class)

这没有用,因为 selfMeta

中无法访问

在咨询 documentation 之后,我尝试了这个:

class SchemaModel(models.Model):

    class Meta():
        abstract = True
        managed = True
        db_table = '%(app_label)\".\"%(class)s'

这导致每个模型的 属性 db_table "%(app_label)\".\"%(class)s"

>>> t = Task()
>>> t._meta.db_table
'%(app_label)"."%(class)s'
>>>

我在互联网上没有找到类似的东西。我是不是想做一些不可能或“禁止”的事情?

解决方案

解决方案如elyas answer所示,通过遍历所有__subclasses__()

models.py末尾设置db_table属性
for model in SchemaModel.__subclasses__():
    db_table = '{}s'.format(model._meta.label_lower.replace('.','\".\"'))
    model._meta.original_attrs['db_table'] = db_table
    model._meta.db_table = db_table

我不会说这是禁止的。但我想不出任何方式来声明地做到这一点。然而,有一种方法可以做到这一点。

首先,关于您现有的尝试:

正在访问'self'

db_table = AppConfig.label+'\".\"'+self.__class__.lower()+'s'

加载模型时,Meta class 永远不会创建实例 object,因此没有 self 可供参考。但是即使创建了一个实例 object,db_table 也是一个 attribute of the class object,因此在创建 class object 时对其进行评估,这在任何实例之前object 被创建,所以用这种方式定义 class 属性时无法访问 self

编辑: 正如您提到的,无法通过 AppConfig.label.

访问 app_label

字符串格式

db_table = '%(app_label)\".\"%(class)s'

这些占位符仅在 very specific situation 中定义 ForeignKeyOneToOneField 的字段的 related_namerelated_query_name 属性时使用基地 class.

一个解决方案

正如我所说,我想不出任何声明式的方式来实现这一目标。例如,尝试使用 __qualname__ 是行不通的,因为您每次都会以 SchemaModel.Meta 结束。

但是您可以像这样在 models.py 的底部放置一个 for 循环:

for model in SchemaModel.__subclasses__():
    # Name your db_table here
    db_table = model._meta.app_label + '\".\"' + model._meta.model_name.lower() + 's'
    # Set db_table here
    model._meta.original_attrs['db_table'] = db_table
    model._meta.db_table = db_table
  • 所有 SchemaModel 的 children 都可以使用 built-in __subclasses__() 方法找到。
  • db_table 属性需要在两个地方更新。首先在 _meta 中,它(部分)是通过从 Meta class 复制属性创建的,其次在 _meta.original_attrs 中,其中存储了原始 Meta 属性,并且在迁移期间由 Django 读取。

备选方案

我个人会手动定义 db_table 名称,并简单地进行单元测试以检查所有模型是否符合我提出的任何命名约定。我更喜欢这样,所以如果另一个开发人员关注他们之前从未见过的模型,他们可以根据模型中的声明(和抽象基础 class)获得完整的图片,并且不会对修改它们的操作感到困惑别处。