Home » Django » Django admin, hide a model

Django admin, hide a model

Posted by: admin November 30, 2017 Leave a comment

Questions:

At the root page of the admin site where registered models appear, I want to hide several models that are registered to the Django admin.

If I directly unregister those, I am not able to add new records as the add new symbol “+” dissapears.

How can this be done ?

Answers:

Based on x0nix’s answer I did some experiments. It seems like returning an empty dict from get_model_perms excludes the model from index.html, whilst still allowing you to edit instances directly.

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)

Questions:
Answers:

Got the same problem, here what I came up with.

Like in previous solution – copy index.html from django to your /admin/index.html and modify it like this:

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

And create ModelAdmin subclass:

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

Now any model registered with HiddenModelAdmin subclass won’t show up in admin list, but will be available via “plus” symbol in detail:

class MyModelAdmin(HiddenModelAdmin):
    ...

admin.site.register(MyModel, MyModelAdmin)

Questions:
Answers:

Since Django 1.8, ModelAdmin has got a new method called has_module_permission() which is responsible for displaying a model in admin index.

To hide a model from admin index, just create this method in your ModelAdmin class and return False. Example:

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

Questions:
Answers:

Ugly solution: override admin index template i.e. copy index.html from django to your /admin/index.html and add something like this:

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

Questions:
Answers:

This is an alternative building on top x0nix’s answer, and only if you are happy hiding the the rows with jquery.

Copy pasting from the other answer the part that I reused

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)

Then install django-jquery and then add the following block in your /admin/index.html template:

{% 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 %}

You don’t need to copy paste the whole template, just extend it and override the extrahead block. You’ll need django-apptemplates for the above to work.

Questions:
Answers:

Django 1.2 has new if-statements, meaning that the desired feature could be obtained only by overwriting admin/index.html

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

This is a bad solution, because it doesn’t care about multi-language admins. You could of course add the names of models in all of the supported languages. It’s a good solution because it doesn’t overwrite more than one aspect of core Django functions.

But before changing anything, I think people should think about this…

Essentially the problem is related to having models that one does not wish to use for more than adding an option to a drop-down once in a while. It could effectively be worked around by creating a set of permissions for “not so advanced” users that panic when there are too many models. In case changes in the particular models are required, one can simply log in with the “advanced account”.

Questions:
Answers:

I had lots of model admins to register and hide, if you want a more DRY solution, this worked for me (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)

I guess you could roll it into a utility class if you want to re-use it across apps.

Questions:
Answers:

As of Django 1.8.18, has_module_permission() still has issue. So, in our case we used also the get_model_perms(). Likewise, we need to hide the model for specific user only, but the superuser should be able to access its index entry.

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)