When you create a post in Django admin, the author field usually has to be picked by hand — and one forgotten or wrong pick is enough to corrupt your data. The fix is to override save_model() in ModelAdmin: before saving, automatically assign request.user as the author if the post does not have one yet. This only runs inside the admin and never touches save logic elsewhere, so you can apply it right away to any model with an author field.
The Problem
Picture a blog with several editors writing posts. The admin create form shows every field, including the author picker. Someone in a hurry hits save without checking, and the post may end up under someone else’s name — or, if the field allows blank values, with no author to audit later. Fixing posts by hand wastes time and never prevents the next mistake, because the root cause is in the data-entry flow, not the user.
@admin.register(Post)class PostAdmin(admin.ModelAdmin): list_display = ("title", "author", "created_at") # author is still in the form: the creator must pick it by hand
The Solution
It takes just two steps: remove author from the form with exclude, then override save_model() to assign the author before calling super().save_model(). The condition if not obj.author_id is essential: it only assigns when the post has no author yet, so older posts imported from elsewhere are never overwritten. Since request.user is always available in the admin, you need no extra form, signal, or migration.
from django.contrib import adminfrom .models import Post@admin.register(Post)class PostAdmin(admin.ModelAdmin): list_display = ("title", "author", "created_at") exclude = ("author",) def save_model(self, request, obj, form, change): if not obj.author_id: obj.author = request.user super().save_model(request, obj, form, change)
After saving, the author column in the admin list still shows correctly thanks to list_display, even though the field no longer appears in the edit form. Quick check: create a new post with your own account, save it, and look at the author column — your name should be there without you picking anything.
Apply It to Your Project
In a real project, apply this pattern to every model that has a “creator”: posts, internal notes, support tickets. The general rule: any field the system already knows — like the creator or the creation time — should never be typed in by hand. If you later need to assign authors in an API or an import script, do not rely on save_model — it only runs in the admin — move the logic to a shared model or service instead.
Related Posts
- Django Admin: Customize list_display, list_filter, and search_fields for a Faster, Easier Admin
- Django: Create Bulk Actions in Admin — Approve 100 Posts in 1 Click
- Django Signals: Auto-Create a Profile for New Users with post_save in 5 Minutes
Frequently Asked Questions
Does save_model() run when creating objects via the shell or an API?
No. save_model() is only called by the admin when saving through the admin interface. Objects created via the shell, an API, or an import script go through the model’s save(), not save_model().
Will existing posts that already have an author be overwritten?
No, thanks to the if not obj.author_id condition. Only posts without an author get assigned request.user; posts that already have an author stay untouched.
How do I show the author in the edit form but prevent editing it?
Use readonly_fields = ("author",) instead of exclude. The field still shows in the form but cannot be edited, and save_model() still assigns the author before saving as usual.
If several editors edit the same post, does the author change?
No, because the condition only assigns when the post has no author yet. If you also want to record the “last editor”, add an updated_by field and always assign request.user to it in save_model() without any condition.
💬 Have you tried this in your own project? If you hit an error or have a cleaner way, leave a comment below — I read them all and will fold the best ideas into the post.
References
- Django documentation — ModelAdmin.save_model: https://docs.djangoproject.com/en/stable/ref/contrib/admin/#django.contrib.admin.ModelAdmin.save_model
