restrict django choice field on django admin per user

Question:

I have an exam like this:

class Exam(BaseModel):
    ...
    STATE_CHOICES = (
        (PASS, PASS),
        (FAILED, FAILED),
        (GREAT, GREAT),
    state = models.CharField(max_length=15, choices=STATE_CHOICES, default=PASS)
    ...

Inside Django admin, I want the user with group X to be able to only change the state only from FAILED to PASS.
and users with group Y be able to change the state from FAILED to PASS and PASS to GREAT.
here is my admin.py:

@admin.register(Exam)
class ExamAdmin(NestedModelAdmin):
    list_display = ('state',)

Does anyone know a solution for it?

Asked By: Mehdi Aria

||

Answers:

This might work;

class AdminExamForm(forms.ModelForm):
    ...
    options
    ...

class ExamForm(forms.ModelForm):
    ...
    STATE_CHOICES = (
        (PASS, PASS),
        (FAILED, FAILED),
        (GREAT, GREAT),
    state = forms.CharField(choices=STATE_CHOICES)
    class Meta:
        model = Exam
        fields = ('state',)
    ...

@admin.register(Exam)
class ExamModelAdmin(admin.ModelAdmin):
    ...
    fields = ('state',)
    list_display = ('state',)
    form = ExamForm
    ...

    def get_form(self, request, obj=None, **kwargs):
        if request.user.is_admin or request.user.is_superuser:
            return AdminExamForm
        else:
            return ExamForm

Sorry for giving you a bad example before, didn’t have too much time.

This is how you could access the user, if your exam model has one.

from django.contrib.auth import get_user_model

class Exam(BaseModel):
    ...
    STATE_CHOICES = (
        (PASS, PASS),
        (FAILED, FAILED),
        (GREAT, GREAT),
    state = models.CharField(max_length=15, choices=STATE_CHOICES, default=PASS)
    user = models.ForeignKey(get_user_model(), on_delete=models.RESTRICT)
    ...
    def save(self, *args, **kwargs):
        if self.user.is_admin or self.user.is_superuser:
            ... your logic here
        super(Exam,self).save(*args,**kwargs)

To access the request in the create/save method:

  • Pass it into the kwargs of the create/save method, of the form you want.
  • Then get the request in the create/save method, and do your logic
request = kwargs.get('request',None)

Edit, to get the request into the model’s .save()

Django admin’s save model function literally just calls obj.save()
So if you pass request=request into save like so:

def save_model(self, request, obj, form, change):
    """
    Given a model instance save it to the database.
    """
    obj.save(request=request)

it should work.

Override Admin save:

Override save method of Django Admin

Answered By: nigel239
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.