Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Restricting Django Admin Change Permissions

I am trying to restrict users in groups from being able to change fields their group does not have permission to change. For example:

class StudentReferral(models.Model):
    teacher = models.CharField(max_length=50)
    referral_first_name = models.CharField(max_length=50)
    referral_last_name = models.CharField(max_length=50)
    accepted = models.BooleanField(blank=True)

Now the teachers are all in one user group and the person who accepts or declines the referral is in another user group. The teacher user group should only be able to edit the following fields: teacher, referral_first_name and referral_last_name. The other user group should only be able to edit the accepted field. Both groups should be able to see all of the fields.

Is there something built into django that makes this possible or a custom way of doing this?

like image 460
CF711 Avatar asked Feb 20 '23 19:02

CF711


1 Answers

Override ModelAdmin.get_fieldsets, and do something like the following:

class MyModelAdmin(admin.ModelAdmin):
    ...
    fieldsets = (
        ... # Standard/all fields
    )
    teacher_fieldsets = (
        ... # Only fields teachers can see
    )

    def get_fieldsets(self, request, obj=None):
        if request.user.groups.filter(name='Teachers').exists():
            return self.teacher_fieldsets
        else:
            return super(MyModelAdmin, self).get_fieldsets(request, obj=obj)

EDIT

Sorry, I missed the bit about they should still be able to see all fields, just not edit them. I've left the original answer intact, as it might still be of use to you. For this, though, you'll need to override ModelAdmin.get_readonly_fields:

class MyModelAdmin(admin.ModelAdmin):
    ...
    readonly_fields = ('teacher', 'referral_first_name', 'referral_last_name')
    teacher_readonly_fields = ('accepted',)

    def get_readonly_fields(self, request, obj=None):
        if request.user.groups.filter(name='Teachers').exists():
            return self.teacher_readonly_fields
        else:
            return super(MyModelAdmin, self).get_readonly_fields(request, obj=obj)

I'm assuming here that the choices are only teacher or "other user". If there's other types to consider, or you don't want the fields limited at all in one circumstance, you might want to remove the readonly_fields attribute and replace it with something like other_readonly_fields, and branch accordingly (the default value for readonly_fields is only those fields that have editable=False on the model).

Also, be aware that if a field is required, you can't make it read-only as well. If some of these are required fields, you'll need to override ModelForm.__init__ as well, to make them not required in the circumstances where they will be read-only, which requires some extra hackery (ModelForm doesn't have access to request, normally):

class MyModelForm(forms.ModelForm):
    class Meta:
        model = MyModel

    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request')
        super(MyModelForm, self).__init__(*args, **kwargs)

        if self.request is not None:
            if self.request.user.groups.filter(name='Teachers').exists():
                self.fields['accepted'].required = False

class MyModelAdmin(admin.ModelAdmin):
    form = MyModelForm
    ...
    def get_form(self, request, obj=None, **kwargs):
        ModelForm = super(MyModelAdmin, self).get_form(request, obj=obj, **kwargs)
        class WrapperModelForm(ModelForm):
            def __new__(cls, *args, **kwargs):
                kwargs['request'] = request
                return ModelForm(*args, **kwargs)
        return WrapperModelForm
like image 171
Chris Pratt Avatar answered Mar 05 '23 08:03

Chris Pratt