Reputation: 8722
I have a model with a non-editable field in my models file.
class Table(models.Model):
label = models.CharField(max_length=40, editable=False)
In my admin site, when updating existing Table objects, I can't edit the label. That is fine, this is exactly what I want with this constraint. However, when trying to create an object using the admin site, the field is still hidden, so I can only create Table objects using the shell.
How can I make this field appear only on creation, but on updates, it will be read-only? Thanks.
Upvotes: 3
Views: 4959
Reputation: 71
Approach 1
Make label field presented on creation but completely remove it while updating. We will be using ModelAdmin.get_exclude and ModelAdmin.get_fields hooks to accomplish this.
## models.py
class Table(models.Model):
label = models.CharField(max_length=40) # remove editable option
## admin.py
@admin.register(Table)
class TableAdmin(admin.ModelAdmin):
non_editable_fields = ['label']
def get_exclude(self, request, obj=None):
defaults = super().get_exclude(request, obj=obj) or ()
if obj: # if we are updating an object
defaults = (*defaults, *self.non_editable_fields)
return defaults or None
def get_fields(self, request, obj=None):
defaults = super().get_fields(request, obj=obj)
if obj: # if we are updating an object
defaults = tuple(f for f in defaults if f not in self.non_editable_fields)
return defaults
Approach 2
Make label field presented on both creation and update but make it read only while updating. django admin provides a hook for this functioanlity and it is called ModelAdmin.get_readonly_fields
. You can find the documentation here.
So we can write the following code to create a field which can be presented/added when creating an object but can not be edited any further despite set value is being displayed(through admin site).
## models.py
class Table(models.Model):
label = models.CharField(max_length=40) # remove editable option
## admin.py
@admin.register(Table)
class TableAdmin(admin.ModelAdmin):
def get_readonly_fields(self, request, obj=None):
defaults = super().get_readonly_fields(request, obj=obj)
if obj: # if we are updating an object
defaults = tuple(defaults) + ('label', ) # make sure defaults is a tuple
return defaults
Bonus for Approach 2
Also if you have multiple fields on that table you can use fields property to set the ordering(read only fields which are not specifically ordered will be shown at the end of the field list). Down side for this ordering approach is that you have to remember to reflect model changes to fields
property every time you make a change in your model.
Upvotes: 4
Reputation: 88519
Try to use readonly_fields
in admin.py
file
class TableAdmin(admin.ModelAdmin):
readonly_fields = ('label',)
admin.site.register(Table, TableAdmin)
Upvotes: 4