使用 `type` 动态创建 Django 模型
Posted
技术标签:
【中文标题】使用 `type` 动态创建 Django 模型【英文标题】:Dynamically creating Django models with `type` 【发布时间】:2015-01-22 15:16:09 【问题描述】:我有 20 多个 mysql 表,prm_a
,prm_b
,...具有相同的基本结构但名称不同,我想将它们与 Django 模型类相关联,而无需手动编写每一个。因此,怀着雄心壮志,我想尝试将type()
用作类工厂:
以下作品:
def get_model_meta_class(prm_name):
class Meta:
app_label = 'myapp'
setattr(Meta, 'db_table', 'prm_%s' % prm_name)
return Meta
prm_class_attrs =
'foo': models.ForeignKey(Foo),
'val': models.FloatField(),
'err': models.FloatField(blank=True, null=True),
'source': models.ForeignKey(Source),
'__module__': __name__,
###
prm_a_attrs = prm_class_attrs.copy()
prm_a_attrs['Meta'] = get_model_meta_class('a')
Prm_a = type('Prm_a', (models.Model,), prm_a_attrs)
prm_b_attrs = prm_class_attrs.copy()
prm_b_attrs['Meta'] = get_model_meta_class('b')
Prm_b = type('Prm_b', (models.Model,), prm_b_attrs)
###
但如果我尝试按如下方式生成模型类:
###
prms = ['a', 'b']
for prm_name in prms:
prm_class_name = 'Prm_%s' % prm_name
prm_class = type(prm_class_name, (models.Model,), prm_class_attrs)
setattr(prm_class, 'Meta', get_model_meta_class(prm_name))
globals()[prm_class_name] = prm_class
###
我在type()
行上得到一个奇怪的异常(假设__module__
实际上在prm_class_attrs
字典中):
File ".../models.py", line 168, in <module>
prm_class = type(prm_class_name, (models.Model,), prm_class_attrs)
File ".../lib/python2.7/site-packages/django/db/models/base.py", line 79, in __new__
module = attrs.pop('__module__')
KeyError: u'__module__'
所以我有两个问题:我的第二种方法有什么问题,这甚至是创建我的类模型的正确方法吗?
好的 - 感谢@Anentropic,我看到我的prm_class_attrs
字典中的项目在创建类时被 Python 弹出。我现在让它工作了,但前提是我这样做:
attrs = prm_class_attrs.copy()
attrs['Meta'] = get_model_meta_class(prm_name)
prm_class = type(prm_class_name, (models.Model,), attrs)
如果我将 Meta
类设置为属性,则不会
setattr(prm_class, 'Meta', get_model_meta_class(prm_name))
我真的不知道为什么会这样,但至少我现在可以正常工作了。
【问题讨论】:
因为您没有在for
循环中执行 prm_class_attrs.copy()
,所以 __modules__
在第一次迭代中从字典中弹出
是的 - 就是这样。谢谢!
我不知道你必须提供__module__
!这个问题真的很有帮助!
【参考方案1】:
对于仍然想知道如何做到这一点的任何人,我从 @Anentropic 那里得到了天才的答案,并对其进行了一些修改。
我还将 db 表名更改为更像 django 用来命名模型表的名称(“appname_lowercaseclassname”),方法是从类名中剥离所有非字母字符并将生成的字符串转换为小写。
我在 Django 2.2.6 上测试过
def generate_model(class_name):
clean_name_for_table = ''.join(filter(str.isalpha, class_name)).lower() # Could also use regex to remove all non alphabetic characters.
db_table_name = f"__package___clean_name_for_table"
prm_class = type(
class_name,
(BaseClass,),
# this will get merged with the attrs from GeneratedModelBase.Meta
'Meta': type("Meta",(),'db_table':db_table_name),
'__module__': __name__,
)
globals()[class_name] = prm_class
return prm_class
【讨论】:
【参考方案2】:直接原因是因为您没有在 for
循环中执行 prm_class_attrs.copy()
,所以 __modules__
键在第一次迭代时从字典中弹出
至于为什么这不起作用:
setattr(prm_class, 'Meta', get_model_meta_class(prm_name))
...这与 Django 的 models.Model
有一个元类有关。但这是一个 Python metaclass 自定义模型类的创建,与 Django 模型的 Meta
内部类无关(它只是提供有关模型的“元”信息)。
事实上,尽管在models.py
中定义类时看起来是这样,但生成的类没有Meta
属性:
class MyModel(models.Model):
class Meta:
verbose_name = 'WTF'
>>> MyModel.Meta
AttributeError: type object 'MyModel' has no attribute 'Meta'
(您可以直接访问Meta
类,但别名为MyModel._meta
)
您在models.py
中定义的模型实际上更像是模型类的模板,而不是实际的模型类。这就是为什么当您访问模型实例上的字段属性时,您会获得该字段的 值,而不是字段对象本身。
Django model inheritance 可以简化你正在做的事情:
class GeneratedModelBase(models.Model):
class Meta:
abstract = True
app_label = 'myapp'
foo = models.ForeignKey(Foo)
val = models.FloatField()
err = models.FloatField(blank=True, null=True)
source = models.ForeignKey(Source)
def generate_model(suffix):
prm_class_name = 'Prm_%s' % prm_name
prm_class = type(
prm_class_name,
(GeneratedModelBase,),
# this will get merged with the attrs from GeneratedModelBase.Meta
'Meta': 'db_table', 'prm_%s' % prm_name,
'__module__': __name__,
)
globals()[prm_class_name] = prm_class
return prm_class
prms = ['a', 'b']
for prm_name in prms:
generate_model(prm_name)
【讨论】:
我一直在寻找如何做到这一点的简明具体示例。谢谢!【参考方案3】:您可以使用./manage.py inspectdb
这将为您在settings.py
中指向的数据库打印一个 python 模型文件
Documentation
编辑 对于动态模型,请尝试django-mutant 或查看this link
【讨论】:
我发现如果数据库是静态且固定的,这将起作用,但如果我想添加一个表,我希望代码能够即时生成相应的模型。 @xnx 你肯定要写一堆其他代码才能在你的应用程序中使用表格吗?以上是关于使用 `type` 动态创建 Django 模型的主要内容,如果未能解决你的问题,请参考以下文章
Django使用不完整的模型创建一个有效的ModelForm,以在表单验证后手动添加字段