Home > Software design >  Django admin get_readonly_fields inconsistent behavior
Django admin get_readonly_fields inconsistent behavior

Time:11-10

I have the same request as this other thread, I'm using Django 3.2.8

For my project some fields in the Admin pages will have to become readonly based on the value of one boolean field of that same model object, so I applied the solution that was advised in the other thread above.

class OrdineAdmin(admin.ModelAdmin):

   inlines = [
       FatturaAdmin
   ]
   readonly_fields = ["data"]

   def get_readonly_fields(self, request, obj=None):
       if obj and obj.pagato == True:
           self.readonly_fields = ['utente', 'abbonamento', 'importo', 'giorni_attivi_abbonamento', 'data']
       return self.readonly_fields

as I was expecting, this solution would lead to some inconsistency in the admin panel because one instance of OrdineAdmin would be generated in memory but it wouldn't update on each request. So when I set the field "pagato" to True this method does set all the other fields to readonly as expected but won't reverse its effect if I set that field back to False, even if the saving process through the admin panel succeeds.

That is why I was thinking that something needed to be done inside the __init__() method but I read the answer to this other thread where in relation to inline admin elements it is said that it wouldn't make sense, as opposed to what I believed, because:

It also makes no sense to set values like that in init, since the Modeladmin instance is created once and may persist for a lot more than one request!

So now I'm completely confused. How can I grant an update per each request on the ModelAdmin pages for a consistent behavior of my readonly fields logic?

On my side, I tried to get hold of the object inside __init__() with the get_object() method but I need to provide the object id which I still haven't figured out how to access. But if what I'm reading above is true, it would still be useless to provide the logic to the constructor since instances will last for longer than a request anyway.

Maybe I should address the form with get_form() or in my case get_formsets_with_inlines() and customize those methods? But how would I make sure they get re-generated at each request?

CodePudding user response:

When pagato is set to True, you override the readonly_fields property with this line:

self.readonly_fields = ['utente', 'abbonamento', 'importo', 'giorni_attivi_abbonamento', 'data']

So when pagato is set again to False, get_readonly_fields returns the updated version instead of the original.

Try this:

class OrdineAdmin(admin.ModelAdmin):

   inlines = [
       FatturaAdmin
   ]
   readonly_fields = ["data"]

   def get_readonly_fields(self, request, obj=None):
       if obj and obj.pagato == True:
           return ['utente', 'abbonamento', 'importo', 'giorni_attivi_abbonamento', 'data']
       return self.readonly_fields

Or more concise:

class OrdineAdmin(admin.ModelAdmin):

   inlines = [
       FatturaAdmin
   ]

   def get_readonly_fields(self, request, obj=None):
       if obj and obj.pagato == True:
           return ['utente', 'abbonamento', 'importo', 'giorni_attivi_abbonamento', 'data']
       return ["data"]
  • Related