Динамические модели в Django

24 Апр
2012

В одном проекте мне понадобилось динамически формировать набор моделей для каждой записи из другой модели, реализованной обыкновенным способом. Кроме того, для быстрого развертывания проекта, надо чтобы таблицы к динамическим моделям также создавались командой syncdb. Итак, я хочу поделиться своим рецептом.



Для начала опишем в файле 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 — идентификатор строки модели-донора.
По материалам Хабрахабр.



загрузка...

Комментарии:

Наверх