Динамическое создание моделей Django с "типом"
У меня есть 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__'
Итак, у меня есть два вопроса: что не так с моим вторым подходом, и является ли это вообще правильным способом создания моделей моего класса?
OK-благодаря @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))
Я действительно не знаю, почему это так, но, по крайней мере, я пусть он работает сейчас.2 ответа:
Причина imediate заключается в том, что вы не делаете
prm_class_attrs.copy()
в своем циклеfor
, поэтому ключ__modules__
выскакивает из dict на первой итерацииЧто касается того, почему это не работает:
setattr(prm_class, 'Meta', get_model_meta_class(prm_name))
...это связано с тем, что у Джанго
models.Model
есть метакласс. Но это же ... метакласс Python который настраивает создание класса модели и не имеет ничего общего сMeta
внутренним классом модели 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 наследование модели может немного упростить то, что вы делаете:
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)
Вы можете использовать
./manage.py inspectdb
Это позволит распечатать файл моделей python для БД, на которую вы указываете в вашемsettings.py
документацияEDIT Для динамических моделей попробуйте django-mutant или проверьте эту ссылку