【问题标题】:Making inlines conditional in the Django admin在 Django 管理员中使内联成为条件
【发布时间】:2011-07-21 15:49:49
【问题描述】:

我有一个模型,我希望员工能够在活动日期之前对其进行编辑。像这样:

class ThingAdmin(admin.ModelAdmin):
    model = Thing

    if obj.date < today: #Something like that
        inlines = [MyInline,]

问题是,我无法访问此级别的 obj 实例。我尝试过覆盖 get_formset(),但没有成功。

请指教?

【问题讨论】:

    标签: django django-admin conditional inlines


    【解决方案1】:

    感谢 cmets 对 1.4 的更改。我这里的实现也不是线程安全的,所以它确实应该被删除。

    由于get_formsets 被传递给对象并调用get_inline_instances,我们可以修改这两个函数以作用于对象。

    这应该可行:

    class ThingAdmin(admin.ModelAdmin):
        model = Thing
    
        inlines = [inline]
        other_set_of_inlines = [other_inline]
    
        def get_inline_instances(self, request, obj=None):
            #                                    ^^^ this is new
            inline_instances = []
    
            if obj.date > datetime.date(2012, 1, 1):
                inlines = self.inlines
            else:
                inlines = self.other_set_of_inlines
    
            for inline_class in inlines:
                inline = inline_class(self.model, self.admin_site)
                if request:
                    if not (inline.has_add_permission(request) or
                            inline.has_change_permission(request) or
                            inline.has_delete_permission(request)):
                        continue
                    if not inline.has_add_permission(request):
                        inline.max_num = 0
                inline_instances.append(inline)
            return inline_instances
    
        def get_formsets(self, request, obj=None):
            for inline in self.get_inline_instances(request, obj):
                #                                           ^^^^^ this is new
                yield inline.get_formset(request, obj)
    

    【讨论】:

    • 这在 Django 1.4 中不起作用。 __init__ 似乎不再创建 self.inline_instances,但现在有一个 get_inline_instances() 方法可以显式覆盖。
    • 似乎 Cerin 的评论现在已经过时了,因为这个答案已被修改为使用 get_inline_instances
    • 这对我有用,直到我想根据数据是否填写来显示/隐藏内联。我想说if obj.fieldX and obj.fieldX == "some text": [show inline "I"]。但这给了我管理表单错误,我认为是因为在 POST 完成之前将内联添加到表单中,而不是像我预期的那样在 GET 上。我的解决方法是获取保存的对象并将内联的显示/隐藏基于该对象而不是“obj”参数。即if obj: saved_obj = ModelName.objects.get(pk = obj.pk) 然后检查save_obj 的字段而不是obj 的字段。 (django 1.8)
    • 另外,在 django 1.8 中我不需要覆盖“get_formsets”。
    【解决方案2】:

    在最新版本的 Django 中,您需要覆盖 ModelAdmin.get_formsets。例如

    class MyAdmin(admin.ModelAdmin):
    
        def get_formsets(self, request, obj=None):
            if obj:
                for _ in super(MyAdmin, self).get_formsets(request, obj):
                    yield _
            else:
                for inline in self.get_specific_inlines(request):
                    yield inline.get_formset(request, obj)
    

    【讨论】:

      【解决方案3】:

      我有一个复杂的案例,我尝试的解决方案以意想不到的方式失败(内联中的只读字段问题)。这是我发现的最清晰、最安全的方法:

      class MyAdmin(admin.ModelAdmin):
      
          def add_view(self, request, form_url='', extra_context=None):
              self.inlines = [InlineA, InlineC]
              return super(MyAdmin, self).add_view(request, form_url, extra_context)
      
          def change_view(self, request, object_id, form_url='', extra_context=None):
              self.inlines = [InlineB, InlineC, InlineD]
              return super(MyAdmin, self).change_view(request, object_id, form_url, extra_context)
      

      这适用于 Django 1.4.x。

      【讨论】:

        【解决方案4】:

        我遇到了一种情况,我需要根据您为给定故事使用的管理站点显示内联。

        我能够使用以下代码获得适用于 Django 1.3 的动态内联:

        在 highlight/admin.py 中

        class HighlightInline(generic.GenericTabularInline):
            model = Highlight
            extra = 1
            max_num = 4
            fields = ('order', 'highlight')
            template = 'admin/highlights/inline.html'
        
        class HighlightAdmin(admin.ModelAdmin):
            def regulate_highlight_inlines(self):
                highlights_enabled = Setting.objects.get_or_default('highlights_enabled', default='')
                highlight_inline_instance = HighlightInline(self.model, self.admin_site)
                highlight_found = any(isinstance(x, HighlightInline) for x in self.inline_instances)
                if highlights_enabled.strip().lower() == 'true':
                    if not highlight_found:
                        self.inline_instances.insert(0, highlight_inline_instance)
                else:
                    if highlight_found:
                        self.inline_instances.pop(0)
                print self.inline_instances
        
            def change_view(self, request, object_id, form_url='', extra_context=None):
                self.regulate_highlight_inlines()
                return super(HighlightAdmin, self).change_view(request, object_id)
        
            def add_view(self, request, form_url='', extra_context=None):
                self.regulate_highlight_inlines()   
                return super(HighlightAdmin, self).add_view(request, form_url, extra_context)
        

        在故事/admin.py中

        class StoryAdmin(HighlightAdmin):
        

        需要注意的一点是,我不仅仅是在操作内联类 (HighlightInline),而是在更改内联实例 (HighlightInline(self.model, self.admin_site))。这是因为 django 在 admin 类的初始构建过程中,已经根据内联类列表构建了内联实例列表。

        【讨论】:

          【解决方案5】:

          我认为破解此问题的最简单方法是在get_fieldsget_fieldsets 中调用您的自定义函数,等等,只需在自定义函数中设置self.inlines

          class XXXAdmin(admin.ModelAdmin):
              def set_inlines(self, request, obj):
                  """ hack inlines models according current request.user or obj """
                  self.inlines = []
                  if request.user.is_superuser or request.user is obj.recorder:
                      self.inlines = [AbcInline, ]
          
              def get_fields(self, request, obj=None):
                  self.set_inlines(request, obj)  # NOTICE this line
                  super(XXXAdmin, self).get_fields(request, obj)
          

          【讨论】:

            【解决方案6】:

            现在最简单的方法是覆盖和超级调用 get_inline_instances。

            class ThingAdmin(models.ModelAdmin):
                inlines = [MyInline,]
            
                def get_inline_instances(self, request, obj=None):
                    unfiltered = super(ThingAdmin, self).get_inline_instances(request, obj)
                    #filter out the Inlines you don't want
                    keep_myinline = obj and obj.date < today
                    return [x for x in unfiltered if not isinstance(x,MyInline) or keep_myinline]
            

            这会将 MyInline 在您需要时放入,而不是在您不需要时放入。如果你知道你的类中唯一的内联是 MyInline,你可以让它更简单:

            class ThingAdmin(models.ModelAdmin):
                inlines = [MyInline,]
            
                def get_inline_instances(self, request, obj=None):
                    if not obj or obj.date >= today:
                        return []
                    return super(ThingAdmin, self).get_inline_instances(request, obj)
            

            【讨论】:

              【解决方案7】:

              从 Django 2.2.2(撰写本文时的当前最新版本)开始,我将使用 @aggieNick02 之前提供的解决方案,即覆盖如下所示的 get_inline_instances

              class ThingAdmin(models.ModelAdmin):
                  inlines = [MyInline,]
              
                  def get_inline_instances(self, request, obj=None):
                      if not obj or obj.date >= today: return []
                      return super(ThingAdmin, self).get_inline_instances(request, obj)
              

              我发布这个新答案是因为截至 2019 年 4 月 17 日,this commit,看起来未来推荐的方法是覆盖get_inlines 方法。所以在以后的版本中,这个问题的解决方案可能类似于下面的代码,它允许您指定不同的内联集并根据条件使用它们。

              class ThingAdmin(admin.ModelAdmin):
                  model = Thing
              
                  inlines = [inline]
                  other_set_of_inlines = [other_inline]
              
                  def get_inlines(self, request, obj):
                      if obj.date > datetime.date(2012, 1, 1):
                          return self.inlines
                      else:
                          return self.other_set_of_inlines
              

              【讨论】:

              【解决方案8】:

              您可以使用 (django 3.0+) get_inlines 方法。您所要做的就是重写方法并定义您的逻辑,

              class ThingInline(admin.StackedInline):
                  """ inline needs to be returned """
                  models = ThingModel
              
              
              class ThingAdmin(admin.ModelAdmin):
                  model = Thing
                  inlines = []
              
                  def get_inlines(self, request, obj):
                      if obj.date < today:    # the date
                          return [ThingInline]
                      # or else
                      return []
              

              更新: 通过这种方法,我遇到了this issue,因此可以通过覆盖change_view() 方法来代替使用上述方法,

              class ThingAdmin(admin.ModelAdmin):
                  model = Thing
                  inlines = []
              
                  def change_view(self, request, object_id, form_url='', extra_context=None):
                      self.inlines = []
                  
                      try:
                          obj = self.model.objects.get(pk=object_id)
                      except self.model.DoesNotExist:
                          pass
                      else:
                          if obj.date < today:
                              self.inlines = [ThingInline,]
              
                      return super(ThingAdmin, self).change_view(request, object_id, form_url, extra_context)
              

              【讨论】:

                【解决方案9】:

                这个问题的最佳解决方案已经回答here。而不是覆盖get_inline_instances 覆盖change_view 方法。

                def change_view(self, request, object_id, form_url='', extra_context=None):
                    self.inlines = []
                    try:
                        obj = self.model.objects.get(pk=object_id)
                    except self.model.DoesNotExist:
                        pass
                    else:
                        if condition:
                            self.inlines = [InlineClass]
                    return super(AdminClass, self).change_view(request, object_id, form_url, extra_context)
                

                【讨论】:

                  猜你喜欢
                  • 2015-06-21
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-04-10
                  • 2011-06-02
                  • 2013-03-01
                  • 2017-06-28
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多