2015-06-12 2 views
3

У меня есть модель Django, где доступно множество полей. Поэтому мне пришлось написать много свойств «is_something» этого класса, чтобы проверить, равно ли значение экземпляра некоторому значению выбора. Что-то вдоль линий:Динамически добавлять свойства к модели django

class MyModel(models.Model): 
    some_choicefield = models.IntegerField(choices=SOME_CHOICES) 

    @property 
    def is_some_value(self): 
     return self.some_choicefield == SOME_CHOICES.SOME_CHOICE_VALUE 

    # a lot of these... 

Чтобы автоматизировать этот и пощади меня много лишнего кода, я думал о латание экземпляра при создании, с функцией, которая добавляет кучу методов, которые делают проверки. код стал следующим образом (я предполагаю, что есть функция «нормализует», что делает метку выбора годной к употреблению имени функции):

def dynamic_add_checks(instance, field): 
    if hasattr(field, 'choices'): 
     choices = getattr(field, 'choices') 
     for (value,label) in choices: 
      def fun(instance): 
       return getattr(instance, field.name) == value 
      normalized_func_name = "is_%s_%s" % (field.name, normalize(label)) 
      setattr(instance, normalized_func_name, fun(instance)) 

class MyModel(models.Model): 
    def __init__(self, *args, **kwargs): 
     super(MyModel).__init__(*args, **kwargs) 
     dynamic_add_checks(self, self._meta.get_field('some_choicefield') 

    some_choicefield = models.IntegerField(choices=SOME_CHOICES) 

Теперь, это работает, но у меня есть чувство, что есть лучшее способ сделать это. Возможно, во время создания класса (с метаклассами или в методе )? У вас есть какие-то мысли/предложения по этому поводу?

ответ

3

Ну, я не уверен, как это сделать на вашем пути, но в таких случаях я думаю, что путь к простому созданию новой модели, где вы держите свой выбор, и изменить поле на ForeignKey. Это проще для кодирования и управления.

Вы можете найти много информации на базовом уровне в Django docs: Models: Relationships. Там есть много ссылок для расширения по различным темам. Наверное, я считаю, что это просто требует немного воображения и, возможно, проб и ошибок в начале.

+0

Варианты выбора для каждого поля. Вы имеете в виду создание новой модели вместо каждого поля, которое содержит выбор? Разве это не будет излишним с точки зрения выступлений? – mp85

+0

Такое поведение можно моделировать, возможно, проще. Overkill? да и нет, это действительно зависит от многих вещей. – Wtower

+0

Спасибо, это интересный подход, я должен, вероятно, прочитать об этом. Можете ли вы указать мне на любой ресурс, где этот шаблон более подробно объясняется? – mp85

3

Я столкнулся с аналогичной проблемой, когда мне нужно было написать большое количество свойств во время выполнения, чтобы обеспечить обратную совместимость при изменении полей модели. Есть два стандартных способа справиться с этим -

  1. Прежде всего, это использовать пользовательский метакласс в ваших моделях, который наследуется от метакласса по умолчанию.
  2. Во-вторых, следует использовать декораторы класса. Декораторы классов иногда предоставляют легкую альтернативу метаклассам, если только вам не нужно что-то делать до создания класса, и в этом случае вам нужно идти с метаклассами.
+0

Эй, я думаю, у тебя есть правильный подход. Не могли бы вы добавить пример? – jsmedmar

+0

@jsmedmar Я не могу сделать это прямо сейчас, проверьте мой другой ответ https://stackoverflow.com/questions/32091352/in-python-how-to-initialize-a-class-attribute-from-the-return -value-of-a-class/32091606 # 32091606 и посмотреть, помогает ли это – hspandher

+0

Я не подставил из вашего анвва, как я мог динамически добавлять свойства к моим объектам модели ... – jsmedmar

3

Я уверен, вы знаете, что поля Django с предоставленными вариантами автоматически будут иметь функцию отображения.

Скажите у вас есть поле определено как это:

category = models.SmallIntegerField(choices=CHOICES) 

Вы можете просто вызвать функцию с именем get_category_display() для доступа к значению дисплея. Вот исходный код Джанго этой функции:

https://github.com/django/django/blob/baff4dd37dabfef1ff939513fa45124382b57bf8/django/db/models/base.py#L962

https://github.com/django/django/blob/baff4dd37dabfef1ff939513fa45124382b57bf8/django/db/models/fields/init.py#L704

Таким образом, мы можем следовать этому подходу для достижения нашей цели dynamically set property.

Вот мой сценарий, немного отличается от вашего, но до конца это то же самое:

У меня есть два класса, Course и Lesson, класс Lesson имеет ForeignKey поля Course, и я хочу добавить имя свойства cached_course к классу Lesson который будет пытаться получить Course из кэша первыми, и возврат к базе данных, если промах:

Вот типичное решение:

from django.db import models 


class Course(models.Model): 
    # some fields 


class Lesson(models.Model): 
    course = models.ForeignKey(Course) 

    @property 
    def cached_course(self): 
     key = key_func() 
     course = cache.get(key) 
     if not course: 
      course = get_model_from_db() 
      cache.set(key, course) 
     return course 

Оказывается, у меня есть так много полей ForeignKey кэшировать, так вот код следующего аналогичного подхода особенность Джанго get_FIELD_display:

from django.db import models 
from django.utils.functional import curry 


class CachedForeignKeyField(models.ForeignKey): 

    def contribute_to_class(self, cls, name, **kwargs): 
     super(models.ForeignKey, self).contribute_to_class(cls, name, **kwargs) 
     setattr(cls, "cached_%s" % self.name, 
       property(curry(cls._cached_FIELD, field=self))) 


class BaseModel(models.Model): 

    def _cached_FIELD(self, field): 
     value = getattr(self, field.attname) 
     Model = field.related_model 
     return cache.get_model(Model, pk=value) 

    class Meta: 
     abstract = True 


class Course(BaseModel): 
    # some fields 


class Lesson(BaseModel): 
    course = CachedForeignKeyField(Course) 

Изменяя CachedForeignKeyField и перезаписать метод contribute_to_class, наряду с BaseModel класса с методом _cached_FIELD, каждый CachedForeignKeyField автоматически получит свойство cached_FIELD.

Слишком хорошо, чтобы быть правдой, браво!

Смежные вопросы