2011-11-10 9 views
8

Dato un modello denominato MainModel e RelatedModel, dove successivamente ha ForeignKey campo per MainModel:Come mostrare diverse inlines seconda del valore corrente di campo oggetto

class MainModel(models.Model): 
    name = models.CharField(max_length=50) 
    type = models.BooleanField() 

class RelatedModel1(models.Model): 
    main = models.ForeingKey(MainModel): 
    name = models.CharField(max_length=50) 

class RelatedModel2(models.Model): 
    main = models.ForeingKey(MainModel): 
    name = models.CharField(max_length=50) 

e la corrispondente ModelAdmin classi:

class RelatedModel1InlineAdmin(admin.TabularInline): 
    model = RelatedModel1 

class RelatedModel2InlineAdmin(admin.TabularInline): 
    model = RelatedModel2 

class MainModel(admin.ModelAdmin): 
    inlines = [RelatedModel1, RelatedModel2] 

E questo è il comportamento predefinito, si ottiene due inline, uno per ogni modello correlato. La domanda è: come nascondere completamente tutte le inline quando viene creata l'istanza MainModel (s' add_view la ModelAdmin), e per mostrare le inline per RelatedModel1 quando il campo dell'istanza MainModeltype è True, e mostrare le inline per RelatedModel2 quando False.

Stavo per creare un descriptor per l'attributo ModelAdmin.inline_instances, ma mi sono reso conto che ho bisogno di accedere all'istanza dell'oggetto che si sta modificando, ma viene passato come parametri.

Qualsiasi aiuto?

Grazie!

+0

avevo bisogno che qualche tempo fa .. non ha mai trovato la risposta, buona domanda! – juliomalegria

risposta

1

Da sbirciare a contrib.admin.options.py Sembra che sia possibile ignorare ModelAdmin.get_formsets. Nota che il sito di amministrazione popola self.inline_instances allo __init__, quindi probabilmente vorrai seguire e non istanziare la tua inline più e più volte. Non sono sicuro quanto è caro:)

def get_formsets(self, request, obj=None): 
    if not obj: 
     return [] # no inlines 

    elif obj.type == True: 
     return [MyInline1(self.model, self.admin_site).get_formset(request, obj)] 

    elif obj.type == False: 
     return [MyInline2(self.model, self.admin_site).get_formset(request, obj)] 

    # again, not sure how expensive MyInline(self.model, self.admin_site) is. 
    # the admin does this once. You could instantiate them and store them on the 
    # admin class somewhere to reference instead. 

L'amministratore originale get_formsets utilizza generatori - si potrebbe anche per imitare più da vicino l'originale:

def get_formsets(self, request, obj=None): 
    for inline in self.inline_instances: 
     yield inline.get_formset(request, obj) 
+0

Si scopre che questo non funziona, perché questo in ModelAdmin è 'change_view'/'add_view':' per FormSet, inline in zip (self.get_formsets (request, obj), self.inline_instances): 'Quindi, una lista con viene restituito un formset, ma quando viene "zippato" con un elenco di inline, non sempre il formset restituito corrisponde al primo inline nell'elenco. –

+0

Ahi, hai ragione! Non sono sicuro di vedere un buon modo per chiamare self.inline_instances. Un lucchetto per rendere una vista alla volta? :(Dang, in realtà ero piuttosto entusiasta di questa idea perché mi sono imbattuta in me stesso –

+0

Che dire di questo: 'get_formsets' dovrebbe restituire le istanze di Formset, quindi, che dire di" disabilitare "quelli che non sono correlati al valore di 'obj.type'? –

4

@Yuji 'Tomita' Tomitayou l'idea era buono, ho avuto lo stesso, ma una volta provando, ho capito che devi anche rimuovere la chiave specifica da self.inlines perché nel metodo change_view e add_viewself.get_inline_instances(request) viene chiamato prima di get_formsets(). Pertanto ho spostato la gestione delle inline al metodo get_form().

Ecco come ho fatto con successo:

class SampleAdmin(ModelAdmin): 
    inlines = [] 

    def get_inlines(self): 
     return [SampleInline, SampleInline2] 

    def get_form(self, request, obj=None, **kwargs): 
     # due to django admin form fields caching you must 
     # redefine inlines on every `get_form()` call 
     if (obj): self.inlines = self.get_inlines() 
     for inline in self.inlines: 
      # Here change condition based on your needs and manipulate 
      # self.inlines as you like (remove, change, etc). 
      # I used inline.__name__ to detect if this is correct inline 
      # for my obj 
      if obj.CONDITION: 
       self.inlines.remove(inline) 
     return super(SampleAdmin, self).get_form(request, obj, **kwargs) 
0

Ecco un pezzo di codice che ho scritto quando mi trovai di fronte lo stesso problema. È un po 'brusco, credo, ma è molto agile e dovrebbe essere adatto a tutti i casi.

class MyModelAdmin(admin.ModelAdmin): 
    def __init__(self, *args, **kwargs): 
     super(MyModelAdmin, self).__init__(*args, **kwargs) 
     self.inline_instances_hash = {} 
     for inline_class in self.inlines: 
      for inline_instance in self.inline_instances: 
       if isinstance(inline_instance, inline_class): 
        break 
      self.inline_instances_hash[inline_class] = inline_instance 

    def get_inline_instance(self, inline_class): 
     return self.inline_instances_hash[inline_class] 

    def get_form(self, request, obj=None, **kwargs): 
     if obj: 
      self.inline_instances = [] 
      if self.CONDITION: 
       self.inline_instances.append(self.get_inline_instance(
        THE_INLINE_CLASS_I_WANT)) 
      #... 
     else: 
      self.inline_instances = self.inline_instances_hash.values() 
3

Mi rendo conto che questa domanda è un po 'vecchia e il codebase è cambiato un po'; c'è un punto preciso per scavalcare le cose al momento: get_inline_instances. È possibile effettuare ciò:

class MainModelAdmin(models.ModelAdmin): 
    inlines = [RelatedModel1InlineAdmin,RelatedModel2InlineAdmin] 

    def get_inline_instances(self, request, obj=None): 
     #Return no inlines when obj is being created 
     if not obj: 
      return [] 
     unfiltered = super(MainModelAdmin, self).get_inline_instances(request, obj) 
     #filter out the Inlines you don't want 
     if obj.type: 
      return [x for x in unfiltered if isinstance(x,RelatedModel1InlineAdmin)] 
     else: 
      return [x for x in unfiltered if isinstance(x,RelatedModel2InlineAdmin)] 
0

Questo ha funzionato per me mentre cercavo una risposta allo stesso problema in questo vecchio post. Espandere la risposta di darklow, penso che puoi semplicemente sovrascrivere get_inline_instances e aggiungere un controllo extra basato sul tuo tipo.

  1. Aggiungi un metodo di tipo booleano controllo in voi modellare

    classe MainModel (models.Model):

    name = models.CharField(max_length=50) 
    
    type = models.BooleanField() 
    
    def is_type1(self): 
    
        return type=="some value" 
    
    def is_type2(self): 
        return type=="some value" 
    
  2. Aggiungi base di esempio in linea al momento del check tipo - Semplicemente copiare e incollare le get_inline_insances metodo dalla classe genitore nella classe admin.ModelAdmin e aggiungi il blocco if per verificare il tipo di modello come mostrato sotto

    classe MyModelAdmin (admi n.ModelAdmin):

    inlines = [RelatedModel1, RelatedModel2] 
    
    def get_inline_instances(self, request, obj=None): 
        inline_instances = [] 
        if not obj: 
         return [] 
        for inline_class in self.inlines: 
         inline = inline_class(self.model, self.admin_site) 
         if request: 
          if not (inline.has_add_permission(request) or 
             inline.has_change_permission(request, obj) or 
             inline.has_delete_permission(request, obj)): 
           continue 
          if not inline.has_add_permission(request): 
           inline.max_num = 0 
         if obj.is_type1() and isinstance(inline,RelatedModel1InlineAdmin): 
          inline_instances.append(inline) 
         if obj.is_type2() and isinstance(inline,RelatedModel2InlineAdmin): 
          inline_instances.append(inline) 
    
        return inline_instances 
    
Problemi correlati