Django admin – make all fields readonly

Question:

I’m trying to make all fields readonly without listing them explicitly.

Something like:

class CustomAdmin(admin.ModelAdmin):
    def get_readonly_fields(self, request, obj=None):
        if request.user.is_superuser:
            return self.readonly_fields

        return self.fields

The problem is CustomAdmin.fields is not set at this point.

Any ideas?

Asked By: yprez

||

Answers:

You could iterate through the model meta fields:

def get_readonly_fields(self, request, obj=None):
    if obj:
        self.readonly_fields = [field.name for field in obj.__class__._meta.fields]
    return self.readonly_fields
Answered By: Hedde van der Heide

Ok, now there’s this:

class CustomAdmin(admin.ModelAdmin):
    def get_readonly_fields(self, request, obj=None):
        # ...

        return [f.name for f in self.model._meta.fields]

Still looking for a less ugly way.

Answered By: yprez

Careful, self.model._meta.fields are not necessarily the same fields that CustomAdmin has!

“All fields of the Admin” would look more like this:

from django.contrib import admin
from django.contrib.admin.utils import flatten_fieldsets

class CustomAdmin(admin.ModelAdmin):
    def get_readonly_fields(self, request, obj=None):
        if request.user.is_superuser:
            return self.readonly_fields

        if self.declared_fieldsets:
            return flatten_fieldsets(self.declared_fieldsets)
        else:
            return list(set(
                [field.name for field in self.opts.local_fields] +
                [field.name for field in self.opts.local_many_to_many]
            ))
Answered By: Danny W. Adair

With get_fieldsets you get all fields from the form

def get_readonly_fields(self, request, obj=None):
    readonly = []
    for fs in self.get_fieldsets(request, obj):
        if len(fs) > 1:
            readonly += fs[1].get('fields', [])
    return readonly
Answered By: pgranger

For Inlines (Tab or Stack)

def get_readonly_fields(self, request, obj=None):
    fields = []
    for field in self.model._meta.get_all_field_names():
        if field != 'id':
            fields.append(field)
    return fields

def has_add_permission(self, request):
    return False
Answered By: Ohad the Lad

This worked for me with Django 1.10

def get_readonly_fields(self, request, obj=None):
    if request.user.is_superuser:
        return self.readonly_fields

    return list(set(
        [field.name for field in self.opts.local_fields] +
        [field.name for field in self.opts.local_many_to_many]
    ))
Answered By: user2111922

My requirement was similar . I needed only one field to be shown as read-only . And this worked fine:

class ChoiceInline(admin.TabularInline):
    model = Choice
    extra = 1
    fields = ['choice_text', 'votes']
    readonly_fields = ['votes']

class QuestionAdmin(admin.ModelAdmin):
    #fields = ['pub_date', 'question_text']
    fieldsets = [
        (None, {'fields': ['question_text']}),
        ('Date Information', {'fields': ['pub_date']}),
    ]
    search_fields = ['question_text']


    inlines = [ChoiceInline]

Refer: C:Python27Libsite-packagesdjangocontribadminoptions.py

Since django 2.1, you can prevent editing, while allowing viewing, by returning False from the ModelAdmin‘s has_change_permission method, like this:

class CustomAdmin(admin.ModelAdmin):
    def has_change_permission(self, request, obj=None):
        return False

(This will not work before django 2.1, as it will also deny permission to any user trying only to view.)

@admin.register(Hero)
class HeroAdmin(admin.ModelAdmin, ExportCsvMixin):
    ...
    readonly_fields = ["father", "mother", "spouse"]

reference :
https://books.agiliq.com/projects/django-admin-cookbook/en/latest/changeview_readonly.html

Answered By: ali Saen

If someone still lookign for better way, you can use it like this:

@admin.register(ClassName)
class ClassNameAdmin(admin.ModelAdmin):
    readonly_fields = [field.name for field in ClassName._meta.fields]

ClassName is your Model class.

Answered By: B. Okba

Say you have defined user_mode as;

Admin, Customers and Staff

If you’d like to deny a staff (and of course, customers) the privilege of deleting a customer, product, order etc…

Your code goes;

def get_readonly_fields(self, request: HttpRequest, obj=None):
    if request.user.user_mode != "Admin":
        return self.readonly_fields + ['user_mode']
    return super().get_readonly_fields(request, obj)

where user_mode = a model field holding the type of user.

N.B: my code uses "Pylance" (like typescript in JS)

Answered By: Chukwunazaekpere
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.