0

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?

Mehdi Aria
  • 99
  • 9
  • https://stackoverflow.com/questions/46564705/how-to-limit-the-number-of-choices-in-django-admin Have a look at this. The example here works with querysets, but you can replace the queryset with a plain old list like above. – nigel239 Aug 21 '22 at 10:31
  • Checking this topic for hours and still cannot understand the solution to my problem. @nigel239 – Mehdi Aria Aug 21 '22 at 16:21

1 Answers1

1

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

nigel239
  • 1,485
  • 1
  • 3
  • 23
  • Thanks but how can you get user from and show restrict the choices or this model for group of users? – Mehdi Aria Aug 22 '22 at 14:42
  • 1
    @MehdiAria Edited my answer, sorry. – nigel239 Aug 22 '22 at 19:10
  • 1
    Thanks. Is this possible to make this restriction through models instead of ModelAdmin? – Mehdi Aria Aug 22 '22 at 20:26
  • 1
    Ehhm, yes! That is possible. You would need to override the save method -> override save method -> check if the instance's user is a superuser -> if the user is not a superuser, simply don't save the object (don't call `super().save()`). or alternatively use model object permissions. But, you would also have to implement these checks anywhere else, would be weird if a user saw a create page, that they couldn't create anything on. @MehdiAria – nigel239 Aug 22 '22 at 23:30
  • But how can I access to request and user inside that method? can you replace the answer with that one, please? @nigel239 – Mehdi Aria Aug 23 '22 at 11:32
  • @MehdiAria You don't nescessarily need the request. If the Exam model has a `user` field, you can use this to check. – nigel239 Aug 23 '22 at 11:34
  • It doesn't have the user field @nigel239 – Mehdi Aria Aug 23 '22 at 11:41
  • Can you find a way to override the create method of the admin form? You can then pass the request object into the kwargs of `.create(request=request)`, and then in the `.create` method you add: `request = kwargs.get('request',None)` – nigel239 Aug 23 '22 at 11:42
  • I prefer to don't use the admin forms for that – Mehdi Aria Aug 23 '22 at 11:54
  • @MehdiAria it doesn't matter which form, but this should work for both. – nigel239 Aug 23 '22 at 11:54
  • Dude this save self doesn't have any user – Mehdi Aria Aug 23 '22 at 12:11
  • You don't need a user. https://stackoverflow.com/questions/3394835/use-of-args-and-kwargs You could also do this with `mymodel.save(request=request)` See https://stackoverflow.com/questions/73408390/django-custom-model-save-method-for-a-non-persisted-attribute/73408412#73408412 – nigel239 Aug 23 '22 at 12:13
  • Thanks so much, @nigel239 but I want to do the job without using forms. I want to access the user in the same method of my model. – Mehdi Aria Aug 23 '22 at 12:24
  • It's literally what I have been telling you @MehdiAria, you can also override the save method, and do the exact same. Just pass `request=request` into the save method, and then inside of `save()` do `request = kwargs.get('request',None)` – nigel239 Aug 23 '22 at 12:24
  • From where I should pass the request? @nigel239 Should I override another method for that inside the model? – Mehdi Aria Aug 23 '22 at 12:36
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/247479/discussion-between-mehdi-aria-and-nigel239). – Mehdi Aria Aug 23 '22 at 12:44