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)
这没有用,因为 self
在 Meta
中无法访问
在咨询 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 中定义 ForeignKey
或 OneToOneField
的字段的 related_name
和 related_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)获得完整的图片,并且不会对修改它们的操作感到困惑别处。
我正在尝试构建我的第二个 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)
这没有用,因为 self
在 Meta
在咨询 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
.
字符串格式
db_table = '%(app_label)\".\"%(class)s'
这些占位符仅在 very specific situation 中定义 ForeignKey
或 OneToOneField
的字段的 related_name
和 related_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)获得完整的图片,并且不会对修改它们的操作感到困惑别处。