Dynamically creating Django models with `type`

I have 20 + MySQL-table prm_a

, prm_b

... with the same basic structure, but with different names, and I would like to relate them to the classes Django classes without writing each of them manually. So, feeling ambitious, I thought I would try to use type()

as a factory class:

The following works:

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 model classes like this:

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

      

I am getting a curious exception on the line type()

(given that __module__

there is, in fact, a dictionary 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__' 

      

So I have two questions: what is wrong with my second approach, and is this even the correct way to create my class models?

OK - thanks to @Anentropic I can see the items in my dictionary are prm_class_attrs

popped by Python when classes are created. And now it works for me, 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)

      

not if I set the class Meta

as an attribute with

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

      

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

+3


source to share


2 answers


The exclusive reason is that you are not executing prm_class_attrs.copy()

in your loop for

, so the key is __modules__

popped out of the dict on the first iteration

How why doesn't it work:

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

... this is because Django models.Model

has a metaclass. But this is a Python meta-trick that customizes the creation of the model class and has nothing to do with Meta

Django's internal model class (which simply provides "meta" information about the model).

In fact, no matter what it looks like, when you define a class in models.py

, the resulting class has no attribute Meta

:



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

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

      

(You can access the class Meta

directly, but with an alias like MyModel._meta

)

The model you define in models.py

is indeed more of a template for the 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 model inheritance can simplify what you do a bit:

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)

      

+4


source


You can use ./manage.py inspectdb

This will print the python models file for the database you point to in your settings.py

Documentation



EDIT For dynamic models try django-mutant or visit this link

+1


source







All Articles