xnx*_*xnx 4 python django metaclass python-2.7
我有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)
###
Run Code Online (Sandbox Code Playgroud)
但是如果我尝试生成模型类如下:
###
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
###
Run Code Online (Sandbox Code Playgroud)
我在线上得到一个好奇的异常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__'
Run Code Online (Sandbox Code Playgroud)
所以我有两个问题:我的第二种方法有什么问题,这是创建我的班级模型的正确方法吗?
好的 - 感谢@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)
Run Code Online (Sandbox Code Playgroud)
如果我把这个Meta班级设为一个属性,那就不行了
setattr(prm_class, 'Meta', get_model_meta_class(prm_name))
Run Code Online (Sandbox Code Playgroud)
我真的不知道为什么会这样,但至少我现在正在努力.
中间原因是因为你没有prm_class_attrs.copy()在你的for循环中做,因此__modules__关键是在第一次迭代时从dict中弹出
至于为什么这不起作用:
setattr(prm_class, 'Meta', get_model_meta_class(prm_name))
......这与Django models.Model有一个元类的事实有关.但这是一个Python元类,它定制模型类的创建,与MetaDjango模型的内部类无关(它只提供有关模型的'meta'信息).
实际上,尽管在您定义类时它看起来如何,但models.py结果类没有Meta属性:
class MyModel(models.Model):
class Meta:
verbose_name = 'WTF'
>>> MyModel.Meta
AttributeError: type object 'MyModel' has no attribute 'Meta'
Run Code Online (Sandbox Code Playgroud)
(您可以Meta直接访问该类,但别名为MyModel._meta)
您定义的模型models.py实际上更像是模型类的模板,而不是实际的模型类.这就是为什么当您访问模型实例上的字段属性时,您获得该字段的值,而不是字段对象本身.
Django 模型继承可以简化您正在做的事情:
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)
Run Code Online (Sandbox Code Playgroud)