2011-10-25 12 views
14

Diciamo che ho un po 'modello di Django che è una classe base astratta:Come si cambiano gli argomenti dei campi nelle sottoclassi del modello Django?

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

    class Meta: 
     abstract = True 

e ha due classi derivate, dove mi piacerebbe il valore di default del campo per essere diverso per ogni classi figlie. Non posso semplicemente ignorare il campo

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

perché Django non ti consente di ignorare i campi in sottoclassi. Ho visto post sul tentativo di modificare le scelte disponibili, ma in questo caso mi interessa soprattutto la modifica del valore predefinito. Qualche consiglio?

risposta

7

Il problema con la ridefinizione del metodo di salvataggio come suggerito nell'altra risposta è che il valore non verrà impostato finché non si salva l'oggetto del modello. Un'altra opzione che non ha questo problema è quello di ridefinire il __init__ nella classe figlio (classe Bar):

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

Penso che ciò che stai cercando di fare non sia possibile, almeno non in Django. Devi vedere l'ereditarietà di Django come ForeignKey per la super-classe (è praticamente così) e non puoi cambiare il valore predefinito di un attributo in una relazione FK.

Quindi, la cosa migliore che si può fare è ridefinire il metodo save(). Sarebbe qualcosa di simile:

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

E, a Foo (super-classe):

value = models.IntegerField(blank=True) 

per evitare problemi NOT NULL con il database.

+0

Se dovessi semplicemente per definire i valori di default per tutti gli elementi della classe base, potremmo poi basta sovrascrivere il metodo save per tutte le classi figlie o le impostazioni predefinite definite nella classe base fanno sì che le variabili di istanza non siano mai vuote? (Cioè, se definisco un valore predefinito nella classe genitore, è quel valore predefinito aggiunto quando salvi o quando inizializzi l'oggetto?) – Zxaos

+1

Ancora, devi vedere l'ereditarietà di Django come FK per la super-classe, quindi se la super-classe ha un campo con un valore predefinito, il campo nella sottoclasse avrà anche il campo con il valore predefinito, perché il campo non è in realtà dalla sottoclasse ma dalla super-classe. – juliomalegria

0

Questo definisce un metaclass (e una classe base) che fornisce tutte le sottoclassi un campo di una data tipo, con impostazioni predefinite che possono essere impostati nella sottoclasse, ma non hanno di essere:

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

meno che non si dispone di un gruppo di sottoclassi e/o multiple metodi/attributi e le ipotesi che legano nella BaseTextFieldClass, questo è probabilmente eccessivo ... ma dovrebbe fare quale OP richiesto.

1

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

si può effettivamente fare questo nel modo seguente:

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... 
Problemi correlati