2011-10-25 2 views
14

Скажем, у меня есть модель Django, которая является абстрактным базовым классом:Как изменить аргументы поля в подклассах модели Django?

class Foo(models.Model): 
    value=models.IntegerField() 

    class Meta: 
     abstract = True 

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

class Bar(Foo): 
    value=models.IntegerField(default=9) 

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

ответ

7

Проблема с переопределением метода сохранения, как предлагается в другом ответе, заключается в том, что ваше значение не будет установлено до тех пор, пока вы не сохраните объект модели. Другого варианта, который не имеет эту проблемы является переопределение __init__ в дочернем классе (Bar класс):

def __init__(self, *args, **kwargs): 
    if 'value' not in kwargs: 
     kwargs['value'] = 9 
    super(Bar, self).__init__(*args, **kwargs) 
2

Я думаю, что то, что вы пытаетесь сделать, невозможно, по крайней мере, не в Django. Вы должны видеть наследование в Django как ForeignKey для суперкласса (это в значительной степени это), и вы не можете изменить значение по умолчанию для атрибута в отношении FK.

Итак, самое лучшее, что вы могли бы сделать, это переопределить метод save(). Было бы что-то вроде:

def save(self, *args, **kwargs): 
    if not self.value: 
     self.value = 9 
    super(Bar, self).save(*args, **kwargs) 

, и в Foo (супер-класса):

value = models.IntegerField(blank=True) 

избежать NOT NULL проблемы с базой данных.

+0

Если бы я просто определить значения по умолчанию для всех элементов в базовом классе, мы могли бы тогда просто переопределить метод сохранения для все дочерние классы или значения по умолчанию, определенные в базовом классе, заставляют переменные экземпляра никогда не быть пустыми? (То есть, если я определяю значение по умолчанию в родительском классе, это значение по умолчанию добавлено при сохранении или при инициализации объекта?) – Zxaos

+1

Опять же, вы должны увидеть наследование в Django как FK для суперкласса, поэтому если у суперкласса есть поле со значением по умолчанию, то в поле в подклассе также будет поле со значением по умолчанию, потому что поле фактически не относится к подклассу, а из суперкласса. – juliomalegria

0

Это определяет metaclass (и базовый класс), который обеспечивает все подклассы поле данных типа, со значениями по умолчанию, которые могут быть установлены в подклассе, но не должны быть:

from django.db import models 
class DefaultTextFieldMetaclass(models.base.ModelBase): 
    DEFAULT_TEXT = 'this is the metaclass default' 

    def __new__(mcs, name, parents, _dict): 
     if not (('Meta' in _dict) and hasattr(_dict['Meta'], 'abstract') and _dict['Meta'].abstract): 
      # class is concrete 
      if 'DEFAULT_TEXT' in _dict: 
       default = _dict['DEFAULT_TEXT'] 
      else:  # Use inherited DEFAULT_TEXT if available 
       default_set = False 
       for cls in parents: 
        if hasattr(cls, 'DEFAULT_TEXT'): 
         default = cls.DEFAULT_TEXT 
         default_set = True 
       if not default_set: 
        default = mcs.DEFAULT_TEXT 
      _dict['modeltext'] = models.TextField(default=default) 
     return super(DefaultTextFieldMetaclass, mcs).__new__(mcs, name, parents, _dict) 


class BaseTextFieldClass(models.Model): 
    class Meta(object): 
     abstract = True 
    __metaclass__ = DefaultTextFieldMetaclass 
    DEFAULT_TEXT = 'superclass default' 


class TextA(BaseTextFieldClass): 
    DEFAULT_TEXT = 'A default for TextA' 

class TextB(BaseTextFieldClass): 
    DEFAULT_TEXT = 'The default for TextB' 
    number = models.IntegerField(default=43) 

class TextC(BaseTextFieldClass): 
    othertext = models.TextField(default='some other field') 

Если у вас есть куча подклассов и/или множественные методы/атрибуты и допущения, которые связывают в BaseTextFieldClass, это вероятно, будет излишним ... но он должен делать то, что запросил ОП.

1

См https://stackoverflow.com/a/6379556/15690:

Вы действительно можете сделать это следующим образом:

class BaseMessage(models.Model): 
    is_public = models.BooleanField(default=False) 
    # some more fields... 

    class Meta: 
     abstract = True 

BaseMessage._meta.get_field('is_public').default = True 

class Message(BaseMessage): 
    # some fields... 
Смежные вопросы