【问题标题】:Django admin, hide a modelDjango 管理员,隐藏模型
【发布时间】:2011-01-26 18:23:36
【问题描述】:

在出现注册模型的管理站点的根页面,我想隐藏几个注册到 Django admin 的模型。

如果我直接取消注册,我将无法添加新记录,因为添加新符号“+”消失了。

如何做到这一点?

【问题讨论】:

    标签: django django-admin


    【解决方案1】:

    适用于 Django 1.8 及更高版本

    从 Django 1.8 开始,ModelAdmin 有了一个名为 has_module_permission() 的新方法,它负责在管理索引中显示模型。

    要从管理索引中隐藏模型,只需在 ModelAdmin 类中创建此方法并返回 False。示例:

    class MyModelAdmin(admin.ModelAdmin):
        ...
        def has_module_permission(self, request):
            return False
    

    【讨论】:

    • 不幸的是has_module_permission 会影响整个应用程序,而不仅仅是一个模型。因此,将此添加到应用程序中的模型会导致应用程序模型列表 (/admin/app_label/) 中出现 403 Forbidden。见django/contrib/admin/sites.py
    • @F* 我认为这是一个错误。我在 Django 的 IRC 频道上问过这个问题,那里的一些人同意这种行为是不受欢迎的。
    • @F* 假设管理索引页面仍然链接到 /admin/,则可以通过 return request.path!='/admin/' 之类的方式规避该错误。不幸的是,这会重新启用应用模型列表中的这些模型。
    • 我为这个错误打开了ticket here。这是fixed here。希望它应该包含在下一个版本中。
    • 在 Django 1.11 中,深层链接仍然有效,但实体未在主管理屏幕上列出
    【解决方案2】:

    例如,仅不要将 admin.site.register(MyModel, MyModelAdmin) 放在 admin.py 中。正常注册模型。

    【讨论】:

      【解决方案3】:

      从 Django 1.8.18 开始,has_module_permission() 仍然存在问题。所以,在我们的例子中,我们也使用了get_model_perms()。同样,我们只需要为特定用户隐藏模型,但superuser 应该能够访问其索引条目。

      class MyModelAdmin(admin.ModelAdmin):
          def get_model_perms(self, request):
              if not request.user.is_superuser:
                  return {}
              return super(MyModelAdmin, self).get_model_perms(request)
      
      admin.site.register(MyModel, MyModelAdmin)
      

      【讨论】:

        【解决方案4】:

        我有很多模型管理员要注册和隐藏,如果您想要更 DRY 的解决方案,这对我有用(Django 1.10,Python 3.5)

        # admin.py
        
        def register_hidden_models(*model_names):
            for m in model_names:
                ma = type(
                    str(m)+'Admin',
                    (admin.ModelAdmin,),
                    {
                        'get_model_perms': lambda self, request: {}
                    })
                admin.site.register(m, ma)
        
        register_hidden_models(MyModel1, MyModel2, MyModel3)
        

        如果您想跨应用重用它,我想您可以将它放入实用程序类中。

        【讨论】:

          【解决方案5】:

          基于x0nix's answer我做了一些实验。似乎从 get_model_perms 返回一个空字典会从 index.html 中排除模型,同时仍然允许您直接编辑实例。

          class MyModelAdmin(admin.ModelAdmin):
              def get_model_perms(self, request):
                  """
                  Return empty perms dict thus hiding the model from admin index.
                  """
                  return {}
          
          admin.site.register(MyModel, MyModelAdmin)
          

          【讨论】:

          • 同意。只有当我不想更改代码时,这才是问题。我的意思是,我有一个基础应用程序,我想保持干净,不依赖其他应用程序。我将这些依赖项保存在派生的特定于项目的应用程序中。现在我希望管理界面只显示派生应用程序,而不是基础应用程序。 Django 要求将基本应用程序列在 settings/INSTALLED_APPS 中,以便派生应用程序工作。显然,基本应用程序不应该显示,但同时我不想让它保持不变和可重复使用。请参阅 [这里](Stack Exchange/questions/13923968/)。
          • 更短的方式:get_model_perms = lambda self, req: {}
          • 如果我想隐藏某个 userAdmin 的模型怎么办?
          • 小心这个解决方案 - 即使链接消失,用户也可以像这样跳转到对象本身:/admin/main/comment/2333/change/
          【解决方案6】:

          这是基于 x0nix 答案的替代构建,并且只有当您乐于使用 jquery 隐藏行时。

          从其他答案复制粘贴我重复使用的部分

          class HiddenModelAdmin(admin.ModelAdmin):
          def get_model_perms(self, *args, **kwargs):
              perms = admin.ModelAdmin.get_model_perms(self, *args, **kwargs)
              perms['list_hide'] = True
              return perms
          
          class MyModelAdmin(HiddenModelAdmin):
          ...
          
          admin.site.register(MyModel, MyModelAdmin)
          

          然后安装django-jquery,然后在您的/admin/index.html模板中添加以下块:

          {% extends "admin:admin/index.html" %}
          
          {% block extrahead %}
              <script type="text/javascript" src="{{ STATIC_URL }}js/jquery.js"></script>
              {% if app_list %}
                <script type="text/javascript">
                  $(function(){
                    {% for app in app_list %}
                      {% for model in app.models %}
                          {% if model.perms.list_hide %}
                              $('div.app-{{ app.app_label }}').find('tr.model-{{ model.object_name|lower }}').hide();
                          {% endif %}
                      {% endfor %}
                    {% endfor %}
                  });
               </script>
             {% endif %}
          {% endblock %}
          

          您不需要复制粘贴整个模板,只需扩展它并覆盖extrahead 块。您需要django-apptemplates 才能使上述操作生效。

          【讨论】:

            【解决方案7】:

            Django 1.2 有新的 if 语句,这意味着只有通过覆盖 admin/index.html 才能获得所需的功能

            {% if model.name not in "Name of hidden model; Name of other hidden model" %}
                ...
            {% endif %}
            

            这是一个糟糕的解决方案,因为它不关心多语言管理员。您当然可以添加所有受支持语言的模型名称。这是一个很好的解决方案,因为它不会覆盖核心 Django 函数的多个方面。

            但在改变任何事情之前,我认为人们应该考虑一下这个......

            本质上,问题与不希望使用的模型有关,而不仅仅是偶尔在下拉列表中添加一个选项。它可以通过为“不那么高级”的用户创建一组权限来有效地解决,这些用户在模型太多时会感到恐慌。如果需要更改特定型号,只需使用“高级帐户”登录即可。

            【讨论】:

              【解决方案8】:

              遇到同样的问题,这是我想出的。

              像以前的解决方案一样 - 将 index.html 从 django 复制到您的 /admin/index.html 并像这样修改它:

              {% for model in app.models %}
                  {% if not model.perms.list_hide %}
                  <tr>
                  ...
                  </tr>
                  {% endif %}
              {% endfor %}
              

              并创建 ModelAdmin 子类:

              class HiddenModelAdmin(admin.ModelAdmin):
                  def get_model_perms(self, *args, **kwargs):
                      perms = admin.ModelAdmin.get_model_perms(self, *args, **kwargs)
                      perms['list_hide'] = True
                      return perms
              

              现在使用 HiddenModelAdmin 子类注册的任何模型都不会显示在管理列表中,但可以通过“加号”详细显示:

              class MyModelAdmin(HiddenModelAdmin):
                  ...
              
              admin.site.register(MyModel, MyModelAdmin)
              

              【讨论】:

                【解决方案9】:

                丑陋的解决方案:override 管理索引模板,即将 django 中的 index.html 复制到 /admin/index.html 并添加如下内容:

                {% for for model in app.models %}
                    {% ifnotequal model.name "NameOfModelToHide" %}
                    ...
                

                【讨论】: