Django admin site: prevent fields from being edited? - django

is it possible to prevent certain fields to be edited after they've been saved?
They should be editable when the user creates a new item of a certain model but then when they try to open them to edit certain fields are 'blocked'.
thanks

You could override your ModelAdmin's get_readonly_fields to set certain fields readonly:
class MyAdmin(admin.ModelAdmin):
def get_readonly_fields(self, request, obj=None):
if obj: # when editing an object
return ['field1']
return self.readonly_fields

Related

django admin readonly_fields = 'some_field' but create or add cannot edit the same

In the Django admin, I have
readonly_fields = 'some_field'
However, creating or adding cannot edit the field. How can I accomplish this?
def get_readonly_fields(self, request, obj=None):
if obj is None:
return ()
return self.readonly_fields
(the answer, per the original poster #Ma_Mars in the question comment django admin readonly_fields = 'some_field' but create or add cannot edit the same )

Django hide fields only in CREATE object admin page

As subject, i want to hide some fields ONLY when users enter the 'CREATE' admin page for specific model.
I know that change list_display can hide fields in admin page, but it's a global setting which will take affect not only in 'CREATE' admin page, but also in 'UPDATE' admin page.
#admin.register(User)
class UserProfileAdmin(UserAdmin):
def get_fields(self, request, obj=None):
fields = super(UserProfileAdmin, self).get_fields(request, obj)
for field in fields:
if field == 'some_field_name' and obj is None:
continue
yield field
Copied from Exclude fields in Django admin for users other than superuser
def get_fieldsets(self, request, obj=None):
fieldsets = super(MediaAdmin, self).get_fieldsets(request, obj)
if not obj:
fieldsets = (
(u'other', {
'fields': ('media_public_id',)
}),
)
return fieldsets
Django now has a get_exclude method on ModelAdmin for excluding fields programmatically.
It takes the current request and the object (if any) as argument. In your case, the object argument will be none if it's a "create" page so you can use that like so:
class MyModelAdmin(admin.ModelAdmin):
def get_exclude(self, request, obj=None):
excluded = super().get_exclude(request, obj) or [] # get overall excluded fields
if not obj: # if it's a create, so no object yet
return excluded + ['extra_field_to_exclude']
return excluded # otherwise return the default excluded fields if any

Show different model admin list_display and fields set for different user groups

In django I have superuser and group of content editors.
When I edit model as superuser I want be able to edit all fields. And if someone logged in as editor I want to allow him to edit only specific fields.
I have done that with get_form method:
class VideoAdmin(admin.ModelAdmin):
editor_fields = ('description','description_rewrited')
def get_form(self, request, obj=None, **kwargs):
if not hasattr(request.user, 'perms_list'):
request.user.perms_list = request.user.groups.values_list('name',flat=True)
if 'video_description_rewriter' in request.user.perms_list:
print('rewrite fields to normal')
self.fields = self.normaluser_fields
return super(VideoAdmin, self).get_form(request, obj, **kwargs)
It works for me. But when I open video for editing as regular editor it changes superuser fields set to editors fields set.
Open admin model as superuser - http://joxi.ru/zAN5wWMIVjz429
Open admin model as editor - http://joxi.ru/p27LJPZiDNgeA7
Now superuser has the same fields set as editor - http://joxi.ru/L21jko5TW0ydAX
I assume that there is some kind of template caching?
You're setting self.fields to self.normaluser_fields when user is editor, but you aren't setting self.fields back to default value when user is admin. ModelAdmin objects are created on application load and they're shared between all users!
Changing fields back to default value when admin enters page won't solve problem in 100% percent. When admin and non-admin user will try to enter edit page in same time, race condition might occur. Both of them can get same fields.
Instead of rewritting get_form, you can do it simpler by rewritting get_fields method:
def get_fields(self, request):
if not hasattr(request.user, 'perms_list'):
request.user.perms_list = request.user.groups.values_list('name',flat=True)
if 'video_description_rewriter' in request.user.perms_list:
print('rewrite fields to normal')
return self.normaluser_fields
return self.fields
That method won't overwrite any values in ModelAdmin object, so change will be only visible for one user.

Filter django admin by logged in user

I'm new to django.
I'm creating simple app in which I have users enter some data and view it later. I need to make django admin show to the user only the data she enter and non of the other users data.
Is it possible to change it to multiple admin pages?
Thank you
Store a reference to a user in your model.
models.py:
from django.db import models
from django.contrib.auth.models import User
class MyModel(models.Model):
user = models.ForeignKey(User)
... (your fields) ...
Force the current user to be stored in that field (when using admin)
Force any list of these objects to be (additionally) filtered by the current user (when using admin)
Prevent other users from editing (even though they can't see the object in the list they could access its change_form directly)
admin.py:
from django.contrib import admin
from models import MyModel
class FilterUserAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.user = request.user
obj.save()
def get_queryset(self, request):
# For Django < 1.6, override queryset instead of get_queryset
qs = super(FilterUserAdmin, self).get_queryset(request)
return qs.filter(created_by=request.user)
def has_change_permission(self, request, obj=None):
if not obj:
# the changelist itself
return True
return obj.user === request.user
class MyModelAdmin(FilterUserAdmin):
pass # (replace this with anything else you need)
admin.site.register(MyModel, MyModelAdmin)
If you have MyOtherModel with a foreign key "user" just subclass MyOtherModelAdmin from FilterUserAdmin in the same manner.
If you want certain superusers to be able to see anything, adjust queryset() and has_change_permission() accordingly with your own requirements (e.g. don't filter/forbid editing if request.user.username=='me').
In that case you should also adjust save_model() so that your editing doesn't set the user and thus "take away" the object from the previous user (e.g. only set user if self.user is None (a new instance)).
You'll have to save in the user to every item and query each item with that user as search criteria. You'll probably build a base model which all your other models will inherit from. To get you started take a look at row-level permissions in the admin.

Django disable editing (but allow adding) in TabularInline view

I want to disable editing ALL objects within a particular TabularInline instance, while still allowing additions and while still allowing editing of the parent model.
I have this trivial setup:
class SuperviseeAdmin(admin.TabularInline):
model = Supervisee
class SupervisorAdmin(admin.ModelAdmin):
inlines = [SuperviseeAdmin]
admin.site.register(Supervisor, SupervisorAdmin)
I have tried adding a has_change_permission function to SuperviseeAdmin that returns False unconditionally, but it had no effect.
I have tried setting actions = None in SuperviseeAdmin but it had no effect.
What might I be overlooking that could get this to work?
User django admin build in function has_change_permission() and return false to restrict object Edit view.
class SuperviseeAdmin(admin.TabularInline):
model = Supervisee
def has_change_permission(self, request):
return False
class SupervisorAdmin(admin.ModelAdmin):
inlines = [SuperviseeAdmin]
admin.site.register(Supervisor, SupervisorAdmin)
See this solution: Django admin: make field editable in add but not edit
Override get_readonly_fields method:
def get_readonly_fields(self, request, obj=None):
if obj: # obj is not None, so this is an edit
return ['name1',..] # Return a list or tuple of readonly fields' names
else: # This is an addition
return []
You can try creating a separate inline class (see the InlineModelAdmin docs) that uses a custom ModelForm where you can customise the the clean method to throw an error when trying to update:
from django.contrib import admin
from django.core.exceptions import ValidationError
from django.forms import ModelForm
from myapp.models import Supervisee
class SuperviseeModelForm(ModelForm):
class Meta(object):
model = Supervisee
# other options ...
def clean(self):
if self.instance.pk:
# instance already exists
raise ValidationError('Update not allowed')
# instance doesn't exist yet, continue
return super(SuperviseeModelForm, self).clean()
class SuperviseeInline(admin.TabularInline):
model = Supervisee
form = SuperviseeModelForm
class SuperviseeAdmin(admin.ModelAdmin):
inlines = [SuperviseeInline]
just make all your fields readonly_fields in your admin.TabularInline as :
class SuperviseeAdmin(admin.TabularInline):
model = Supervisee
readonly_fields = ('your_field', 'your_field2',)
class SuperviseeAdmin(admin.TabularInline):
model = Supervisee
def __init__(self, *args, **kwargs):
super(SuperviseeAdmin, self).__init__(*args, **kwargs)
self.list_display_links = (None, )