2011-10-25 14 views
14

Digamos que tengo un poco de modelo de Django que es una clase base abstracta:¿Cómo se modifican los argumentos de campo en las subclases del modelo de Django?

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

    class Meta: 
     abstract = True 

y tiene dos clases de derivados, en los que me gustaría el valor por defecto del campo a ser diferente para cada uno de las clases hijas. No puedo simplemente anular el campo

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

porque Django no le permite anular los campos en subclases. He visto publicaciones sobre cómo intentar cambiar las opciones disponibles, pero en este caso me preocupa sobre todo el cambio del valor predeterminado. ¿Algún consejo?

Respuesta

7

El problema de redefinir el método Save como se sugiere en la otra respuesta es que su valor no se establece hasta que se guarda el objeto del modelo. Otra opción que no tiene este problema es redefinir el __init__ en la clase hija (clase bar):

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

creo que lo que estamos tratando de hacer no es posible, al menos no en Django. Tienes que ver la herencia en Django como ForeignKey para la superclase (eso es más o menos), y no puedes cambiar el valor predeterminado de un atributo en una relación FK.

Por lo tanto, lo mejor que podría hacer es redefinir el método save(). Sería algo así como:

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

Y, de Foo (super-clase):

value = models.IntegerField(blank=True) 

para evitar problemas NOT NULL con la base de datos.

+0

Si fuera simplemente para definir valores por defecto para todos los elementos de la clase base, podríamos entonces simplemente reemplazar el método ahorrar para todas las clases secundarias, ¿o los valores predeterminados definidos en la clase base harán que las variables de instancia nunca estén vacías? (Es decir, si defino un valor por defecto en la clase padre, es que el valor por defecto añadió al guardar o al inicializar el objeto?) – Zxaos

+1

Una vez más, hay que ver la herencia en Django como FK a la super-clase, por lo si la superclase tiene un campo con un valor predeterminado, el campo en la subclase también tendrá el campo con el valor predeterminado, ya que el campo no es en realidad de la subclase sino de la superclase. – juliomalegria

0

Esto define una metaclass (y una clase base) que proporciona todas las subclases de un campo de A dado tipo, con los valores predeterminados que se pueden establecer en la subclase, pero no tienen que ser:

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') 

a menos que tenga un montón de subclases y/o múltiples métodos/atributos y supuestos que estén relacionados con el BaseTextFieldClass, este es probablemente excesivo ... pero debería hacer lo que OP solicitó.

1

Ver https://stackoverflow.com/a/6379556/15690:

en realidad se puede hacer esto de la siguiente manera:

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... 
Cuestiones relacionadas