首页 > 解决方案 > Django:用于管理 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 Meta:
    managed = True
    db_table = 'app_name\".\"modelname'

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

不过我想保持干燥。所以我现在要做的是拥有一个自动执行此操作的抽象基类,因此我尝试了以下操作:

class SchemaModel(models.Model):

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

(然后当然继承了基类,我将嵌套Meta类从普通模型中取出)

这不起作用,因为selfMeta

在查阅文档后,我尝试了这个:

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 所示db_tablemodels.py通过遍历所有__subclasses__()

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

标签: python-3.xdjangometaclass

解决方案


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

首先,根据您现有的尝试:

访问“自我”

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

加载模型时,永远不会从类中创建实例对象Meta,因此无需self引用。但是即使创建了一个实例对象,db_table也是类对象的一个​​属性,所以在创建类对象的时候进行求值,是在创建任何实例对象之前,所以这样self定义类属性时是不能访问的。

编辑:正如你所提到的, app_label 不能通过AppConfig.label.

字符串格式

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

这些占位符仅用于在抽象基类中定义or字段的和属性时非常特殊的情况。related_namerelated_query_nameForeignKeyOneToOneField

一个解法

正如我所说,我想不出任何声明性的方式来实现这一点。例如,尝试使用__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可以使用内置方法找到所有的孩子__subclasses__()
  • db_table属性需要在两个地方更新。首先_meta,它(部分)是通过从Meta类中复制属性创建的,其次是存储_meta.original_attrs原始Meta属性并在迁移期间由 Django 读取的位置。

替代解决方案

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


推荐阅读