( cs / en )

How to hide some fields in Django admin for certain users

Let's suppose (pretty common) scenario where majority of users of admin site are eg. 'editors' -- they can as authors create new articles and change them -- however only someone with greater rights can approve articles to be published. For the sake of simplicity let be this someone django admin's superuser and editors be the admin's staff.

First thing you probably want to do is to filter resulting queries in admin according to the logged in user. Today it is a well known technique using queryset() method inside YourModelAdmin class:

def queryset(self, request):
    qs = super(YourModelAdmin, self).queryset(request)
    if request.user.is_superuser:
        return qs
    return qs.filter(owner=request.user)

(Practically it means that currently logged in user is going to get records which belongs to him only, relationship in this case being specified with attribute owner of the type ForeignKey in YourModel model.)

However there are other places in admin where you need to do the same filterning - notably "change lists" where you see all the records for the given model and "change forms" where you can change individual records.

A) Hiding fields in changelist

Since we've already used queryset() "trick" to filter articles according to users, there is no need for editors to see article filter in changelist and be able to search for articles by user name. (Also there may be other things you don't want your editors to see in the changelist.) However these things are of great importance for superuser who can see records from all editors together! Solution? Extend changelist_view() method in YourModelAdmin class:

def changelist_view(self, request, extra_context=None):
    if request.user.is_superuser:
        self.list_display = ('name', 'owner', 'approved', 'some_other_superuser_field', )
        self.list_filter = ('owner__username', )
        self.search_fields = ['name', 'owner__username', ]
    else:
        self.list_display = ('name', 'owner', 'approved', )
        self.list_filter = tuple()
        self.search_fields = []
    return super(YourModelAdmin, self).changelist_view(request, extra_context=extra_context)

In this case setting list_display, list_filter and search_fields directly in YourModelAdmin class has no effect and you MUST supply relevant fields both to the superuser and to the editors. (If you don't do this visibility of these components will be "random", depending probably among other things on the order of users logging to the site.)

B) Hiding fields in changeform

Every article has a field in its model where superuser can approve it (and again for the sake of simplicity let's pretend there's no need for this field to be directly visible in changeform to editors). You definitely don't want editors to mess with this setting :-) How you do this? Extend get_form() method in YourModelAdmin class. But there is one gotcha -- if you use custom fieldsets (or just fields) you MUST exclude hidden fields from your fieldsets too, otherwise you end up with exception from admin site trying to display field which is not available. So the full solution is:

def get_form(self, request, obj=None, **kwargs):
    self.exclude = []
    if request.user.is_superuser:
        self.fieldsets = self.fieldsets_user + self.fieldsets_superuser
    else:
        self.exclude.extend(['approved', 'some_other_superuser_field', ])
        self.fieldsets = self.fieldsets_user
    return super(YourModelAdmin, self).get_form(request, obj, **kwargs)

(Where fieldset_user is the tuple of fields which can be safely visible by editors and fieldset_superuser is another tuple where those fields for superuser eyes only are enumerated.)