2011-11-10 8 views
8

Dado un modelo llamado MainModel y una RelatedModel, donde el último, tiene una ForeignKey campo para MainModel:Cómo mostrar diferentes inlines dependiendo del valor actual campo de objeto

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) 

y la ModelAdmin correspondiente clases:

class RelatedModel1InlineAdmin(admin.TabularInline): 
    model = RelatedModel1 

class RelatedModel2InlineAdmin(admin.TabularInline): 
    model = RelatedModel2 

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

Y ese es el comportamiento predeterminado, se obtienen dos líneas, una para cada modelo relacionado. La cuestión es cómo ocultar completamente todas las inlines cuando se crea la instancia MainModel (el ModelAdmin 's add_view), y para mostrar los inline para RelatedModel1 cuando el campo de la MainModel ejemplo type es True, y mostrar los inline para RelatedModel2 cuando False.

Iba a crear un descriptor para el atributo ModelAdmin.inline_instances, pero me di cuenta de que necesitaba acceso a la instancia del objeto que se estaba editando, pero se pasa como parámetros.

¿Algún ayuda?

Gracias!

+0

lo necesitaba algún tiempo atrás .. nunca encontró la respuesta, buena pregunta! – juliomalegria

Respuesta

1

De echar un vistazo a contrib.admin.options.py Parece que podría anular ModelAdmin.get_formsets. Tenga en cuenta que el sitio de administración rellena self.inline_instances al __init__, por lo que probablemente desee seguir y no crear instancias de sus líneas una y otra vez. No estoy seguro de lo caro que es:)

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. 

El administrador original de get_formsets utiliza generadores - que también podía imitar más con el original:

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

Resulta que esto no funciona, porque esto en 'change_view' /' add_view': 'para FormSet, en línea en zip (self.get_formsets (request, obj), self.inline_instances) de ModelAdmin:' Por lo tanto, una lista con se devuelve un formset, pero cuando se "comprime" con una lista de líneas entrantes, no siempre el formset devuelto corresponde al primer en línea de la lista. –

+0

¡Ay, tienes razón! No estoy seguro de ver un buen camino en esa llamada self.inline_instances. ¿Un candado para representar una vista a la vez? :(Dang. En realidad estaba muy entusiasmado con esta idea porque me he encontrado con ella. –

+0

¿Qué tal esto? 'Get_formsets' debería devolver las instancias de Formset, entonces, ¿qué pasa con" desactivar "aquellas que no están relacionadas con el valor de 'obj.type'? –

4

@Yuji 'Tomita' Tomitayou la idea fue bueno, tuve el mismo pero una vez que lo intenté, me di cuenta de que también debe eliminar una clave específica de self.inlines porque en change_view y add_view se llama al método self.get_inline_instances(request) antes de get_formsets(). Por lo tanto, cambié el manejo de las líneas al método get_form().

Así es como yo lo hice con éxito:

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

Aquí es una pieza de código que escribí cuando me enfrenté con el mismo problema. Creo que es un estilo de fuerza bruta, pero es muy ágil y debería adaptarse a todos los casos.

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

Me doy cuenta de que esta pregunta es un poco antigua y la base de código ha cambiado un poco; hay un punto de limpieza para anular las cosas ahora: get_inline_instances. Puede hacer esto:

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

Esto funcionó para mí mientras buscaba una respuesta al mismo problema en esta publicación anterior. Ampliando la respuesta de darklow, creo que simplemente puede anular get_inline_instances por completo y agregar un cheque adicional según su tipo.

  1. Añadir un método de comprobación de tipo booleano en el que modelar

    MainModel clase (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. Añadir base de instancia en línea en el registro de tipo - simplemente copiar y pegar el get_inline_insances método de la clase principal en su clase admin.ModelAdmin y agregue el bloque if para verificar el tipo de modelo como se muestra a continuación

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