В одном проекте мне понадобилось динамически формировать набор моделей для каждой записи из другой модели, реализованной обыкновенным способом. Кроме того, для быстрого развертывания проекта, надо чтобы таблицы к динамическим моделям также создавались командой syncdb. Итак, я хочу поделиться своим рецептом.
Для начала опишем в файле models.py приложения модель-донор.
Ничего необычного. Фактически, нам отсюда нужны только ID, так что можно ее дополнить любыми полями.
Интересное начинается дальше. Создадим класс, описывающий нужную структуру моделей (см. комментарии по коду)
В данном классе реализуем методы по количеству требуемых моделей
Для облегчения доступа сделаем свойство в модели-доноре, возвращающее объект набора моделей.
Итак структура моделей подготовлена. Чтобы динамические модели создавались по syncdb, надо в файле models.py написать следующий код:
Теперь при первом вызове syncdb будет создана таблица для модели Donor. А после внесения в нее записей и повторного вызова syncdb, для каждой строки из модели Donor будут созданы таблицы rX_any_model_1 и rX_any_model_2, где X — идентификатор строки модели-донора.
Для начала опишем в файле models.py приложения модель-донор.
class Donor(db_models.Model):
title = db_models.CharField(max_length = 100)
Ничего необычного. Фактически, нам отсюда нужны только ID, так что можно ее дополнить любыми полями.
Интересное начинается дальше. Создадим класс, описывающий нужную структуру моделей (см. комментарии по коду)
class ModelSet(object):
def __init__(self, donor_record_id):
self.donor_record_id = donor_record_id
def _create_model(self, model_name = None, fields = None,
meta_opts = None, base_model_class = db_models.Model):
''' Метод возвращает динамически созданный класс модели с указанным
именем, набором полей и мета-опций, унаследованный от указанного класса модели.'''
# имя модели дополняем идентификатором строки модели-донора
name = 'r%s_' % self._donor_record_id + model_name
class Meta:
# обязательно указываем, к какому приложению принадлежит модель
app_label = 'my_application'
db_table = name
# Дополняем метакласс переданными опциями
if meta_opts is not None:
for key, value in meta_opts.iteritems():
setattr(Meta, key, value)
# Словарь атрибутов модели
attrs = {'__module__': self.__class__.__module__,
'Meta' : Meta,
'objects' : db_models.Manager()}
# Добавляем поля к модели
if fields:
attrs.update(fields)
# Создаем класс модели
model = type(name, (base_model_class,), attrs)
return model
В данном классе реализуем методы по количеству требуемых моделей
def anyModel1(self):
fields = {
'title' : db_models.CharField(max_length = 100),
'order_by' : db_models.IntegerField(),
'__unicode__' : lambda self: self.title,
}
meta_opts = {
'verbose_name' : 'AnyObject1',
}
return self._create_model('any_model_1', fields, meta_opts)
def anyModel2(self):
fields = {
# внешний ключ на другую модель из данного набора
'any_object_1' : db_models.ForeignKey(self. anyModel1),
}
meta_opts = {
'verbose_name' : 'AnyObject2',
}
return self._create_model('any_model_2', fields, meta_opts)
Для облегчения доступа сделаем свойство в модели-доноре, возвращающее объект набора моделей.
class Donor(db_models.Model):
…
@property
def model_set(self):
return ModelSet(self.id)
Итак структура моделей подготовлена. Чтобы динамические модели создавались по syncdb, надо в файле models.py написать следующий код:
for rec in Donor.objects.all():
rec.model_set.anyModel1()
rec.model_set.anyModel2()
Теперь при первом вызове syncdb будет создана таблица для модели Donor. А после внесения в нее записей и повторного вызова syncdb, для каждой строки из модели Donor будут созданы таблицы rX_any_model_1 и rX_any_model_2, где X — идентификатор строки модели-донора.