Search code examples
pythondjangodjango-admin

Django Admin readonly_fields: why are two different users seeing the same fields?


I created some code to differentiate between two usergroups in Django admin, resulting in showing all fields readonly or only some of them, which are set directly in the ModelAdmin class.

At first here is the code:

class PersonAdmin(admin.ModelAdmin):
    readonly_fields = ('created_at','created_by',)

def get_form(self, request, obj=None, **kwargs):
    if obj:     # we are in edit mode
        if request.user.is_superuser:
            self.readonly_fields = ()
        else:
            for group in request.user.groups.all():
                if str(group) == 'readonlyuser':
                    allfields = tuple(obj._meta.get_all_field_names())
                    self.readonly_fields = allfields

    return super(PersonAdmin, self).get_form(request, obj, **kwargs)

I divide between the groups and set the fields accordingly. Everything works fine if users from the two groups are not logged in at the same time! After a 'readonly' user logged in, the adminuser will get all fields readonly too.

My inspections provided a solution also: If I put an additional if statement for the adminuser within the for block everything works as expected.

if str(group) == 'adminuser':
    self.readonly_fields = PersonAdmin.readonly_fields

Why is that and what's happening there?

I have no special cache settings made and it happens on the dev server as well as on an Apache with WSGI.

From my understanding request.user.groups.all() should return all groups the currently logged in user belongs to. Where does Django get the allfields (readonly) from, if another user on a different IP and session match this if block?


Solution

  • The ModelAdmin is only instantiated once for all requests that it receives. So when you define the readonly fields like that, you're setting it across the board permanently.

    As long as you're running Django 1.2+, there's a get_readonly_fields method you can use instead for exactly this purpose:

    class MyModelAdmin(admin.ModelAdmin):
        ...
    
        def get_readonly_fields(self, request, obj=None):
            if request.user.is_superuser:
                return super(MyModelAdmin, self).get_readonly_fields(request, obj)
            else:
                return ('created_at', 'created_by')
    

    Remove the readonly_fields attribute from your ModelAdmin or set it to the fields that should be readonly for everyone. Then, in the else block specify all the fields that should be readonly only for non-superusers.