How to show different inlines depending of current object field value

前端 未结 6 1455
不知归路
不知归路 2021-02-14 21:56

Given a model named MainModel and a RelatedModel, where the later has a ForeignKey field to MainModel:

class          


        
相关标签:
6条回答
  • 2021-02-14 22:05

    You need just simply override change_view in ModelAdmin:

    def change_view(self, request, object_id, form_url='', extra_context=None):
        obj = self.model.objects.filter(pk=object_id).first()
        if not obj:
            self.inlines = []
        else:
            if obj.type is True:
                self.inlines = [RelatedModel1InlineAdmin]
            else:
                self.inlines = [RelatedModel2InlineAdmin]
    
        return super().change_view(request,object_id,form_url=form_url,extra_context=extra_context)
    

    that's work for me.

    0 讨论(0)
  • 2021-02-14 22:09

    From peeking at contrib.admin.options.pyLooks like you could override ModelAdmin.get_formsets. Note that the admin site populates self.inline_instances at __init__, so you probably want to follow and not instantiate your inlines over and over. I'm not sure how expensive it is : )

    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.
    

    The original admin get_formsets uses generators - you could too to more closely mimic the original:

    def get_formsets(self, request, obj=None):
        for inline in self.inline_instances:
            yield inline.get_formset(request, obj)
    
    0 讨论(0)
  • 2021-02-14 22:15

    I realize this question's a bit old and the codebase has changed a bit; there's a cleanish point to override things at now: get_inline_instances. You can do this:

    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 讨论(0)
  • 2021-02-14 22:20

    This worked for me while searching for an answer to the same problem in this old post. Expanding upon darklow's answer , I think you can simply override get_inline_instances completely and add an extra check based on your type.

    1. Add a boolean type check method in your model

      class 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. Add inline instance base on type check - Simply copy and paste the get_inline_insances method from the parent class into your admin.ModelAdmin class and add the if block to check the model type as shown below

      class MyModelAdmin(admin.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
      
    0 讨论(0)
  • 2021-02-14 22:23

    @Yuji 'Tomita' Tomitayou the idea was good, i had the same but once trying, i realized you must also remove specific key from self.inlines because in change_view and add_view method self.get_inline_instances(request) is called before get_formsets(). Therefore i moved inlines handling to get_form() method.

    Here is how i sucessfully did it:

    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 讨论(0)
  • 2021-02-14 22:23

    Here is a piece of code I wrote when I was faced with the same problem. It is a bit brute force style, I guess, but is very agile and should suit all cases.

    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()
    
    0 讨论(0)
提交回复
热议问题