Django comes with a fantastic admin area out of the box. Registering and customizing models is a breath of fresh air with its class based structure.
For the unaware, a change form template is shown when you edit/change an existing model instance, or an object.
Setting up context for this blog post.
First: I have set up a base model with soft deletion capabilities by following the excellent tutorial by Adrienne Domingus titled ‘Soft Deletion in Django‘. Written originally in 2017 and last updated in 2020, this is super useful and a highly recommended read. I got to see a ton of nice, production-quality patterns here.
The field is not editable (
editable=False) as per the model definition. It is not shown in any form. The only way to interact with this field is to use class methods.
# model.py class SoftDeletionModel(models.Model): deleted_at = models.DateTimeField( blank=False, null=True, default=None, editable=False )
Second: another tutorial-of-sorts I followed was from the Django Admin Cookbook. It turned up several times in my searches and is generally helpful to point me in the right direction, if nothing else. However, it doesn’t appear to have been updated since Django 2.0.
Its chapter, How to show an uneditable field in admin?, recommends the following solution:
If you have a field withDjango Admin Cookbook by Agiliq, retrieved 2021-05-24.
editable=Falsein your model, that field, by default, is hidden in the change page. This also happens with any field marked as
auto_now_add, because that sets the
editable=Falseon these fields.
If you want these fields to show up on the change page, you can add them to
They share the following snippet next as a demonstration:
@admin.register(Villain) class VillainAdmin(admin.ModelAdmin, ExportCsvMixin): # ... readonly_fields = ["added_on"]
I would highly recommend reading the Django Admin Cookbook article first, before proceeding any further. For the most part, it should take care of your use-case.
The edge-case I encountered, or why the cookbook advice did not work for me.
In my case, I found the snippet to be incomplete. For context, I am running Django 3.2, though that has nothing has to do with the solution. It’s just good to mention for posterity!
As any good Django citizen would, I looked up the API for a
ModelAdmin on the Django documentation site, specifically for the
readonly_fields property, and found the following note:
This makes sense, given my code (continuing the example offered by Django Admin Cookbook):
# admin.py, before the fix @admin.register(Villain) class VillainAdmin(admin.ModelAdmin, ExportCsvMixin): # ... fields = ("name",) readonly_fields = ("deleted_at",)
Since I had defined a custom
fields property, I needed to have the model field on both my
readonly_fields property as well as the
fields property. Once I put this into action, everything worked as I expected.
Continuing the same example, the solution then is:
# admin.py, after the fix @admin.register(Villain) class VillainAdmin(admin.ModelAdmin, ExportCsvMixin): # ... fields = ("name", "deleted_at",) readonly_fields = ("deleted_at",)
And, that’s it!