用`type`动态创建Django模型 [英] Dynamically creating Django models with `type`

查看:68
本文介绍了用`type`动态创建Django模型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有20多个MySQL表,prm_aprm_b,...,它们具有相同的基本结构但名称不同,我想将它们与Django模型类相关联,而无需手动编写每个模型.因此,我有野心,我想尝试使用type()作为班级工厂:

I have 20+ MySQL tables, prm_a, prm_b, ... with the same basic structure but different names, and I'd like to associate them with Django model classes without writing each one by hand. So, feeling ambitious, I thought I'd try my hand at using type() as a class-factory:

以下作品:

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)
###

但是,如果我尝试按如下方式生成模型类:

But if I try to generate the model classes as follows:

###
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词典中):

I get a curious Exception on the type() line (given that __module__ is, in fact, in the prm_class_attrs dictionary):

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__' 

所以我有两个问题:第二种方法有什么问题,这甚至是创建类模型的正确方法吗?

So I have two questions: what's wrong with my second approach, and is this even the right way to go about creating my class models?

好的-感谢@Anentropic,我发现prm_class_attrs字典中的项目在创建类时被Python弹出.现在,它可以正常工作了,但前提是我做到了:

OK - thanks to @Anentropic, I see that the items in my prm_class_attrs dictionary are being popped away by Python when it makes the classes. And I now have it working, but only if I do this:

attrs = prm_class_attrs.copy()
attrs['Meta'] = get_model_meta_class(prm_name)
prm_class = type(prm_class_name, (models.Model,), attrs)

如果我将Meta类设置为带有attribtue的

not if I set the Meta class as an attribtue with

setattr(prm_class, 'Meta', get_model_meta_class(prm_name))

我真的不知道为什么会这样,但是至少我现在已经可以使用它了.

I don't really know why this is, but at least I have it working now.

推荐答案

最直接的原因是因为您没有在for循环中执行prm_class_attrs.copy(),因此__modules__键已从字典上弹出.第一次迭代

The imediate reason is because you are not doing prm_class_attrs.copy() in your for loop, so the __modules__ key is getting popped out of the dict on the first iteration

至于为什么这行不通:

setattr(prm_class, 'Meta', get_model_meta_class(prm_name))

...这与Django的models.Model具有元类这一事实有关.但这是一个 Python元类 ,它可以自定义模型类的创建,而无需执行任何操作带有Django模型的Meta内部类(仅提供有关模型的元"信息).

...it's to do with the fact that Django's models.Model has a metaclass. But this is a Python metaclass which customises the creation of the model class and is nothing to do with the Meta inner-class of the Django model (which just provides 'meta' information about the model).

实际上,尽管您在models.py中定义类时看起来如何,但生成的类却没有Meta属性:

In fact, despite how it looks when you define the class in your models.py, the resulting class does not have a Meta attribute:

class MyModel(models.Model):
    class Meta:
        verbose_name = 'WTF'

>>> MyModel.Meta
AttributeError: type object 'MyModel' has no attribute 'Meta'

(您可以直接访问Meta类,但别名为MyModel._meta)

(You can access the Meta class directly, but aliased as MyModel._meta)

models.py中定义的模型实际上比实际模型类更多地是模型类的模板.这就是为什么当您访问模型实例上的字段属性时,会得到该字段的 value 而不是字段对象本身的原因.

The model you define in models.py is really more of a template for a model class than the actual model class. This is why when you access a field attribute on a model instance you get the value of that field, not the field object itself.

Django 模型继承可以简化一点你在做什么:

Django model inheritance can simplify a bit what you're doing:

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)

这篇关于用`type`动态创建Django模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆