﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
8005	Make it easier to alter a model saved by the admin (e.g. add the user that created or updated it)	simon	nobody	"A very common requirement for customising the admin is to automatically update a created_by or updated_by field with the user object that made changes to an object. This is currently way harder than it should be: http://www.djangosnippets.org/snippets/903/

I propose adding an extra subclassable method that makes it trivial to intercept the model as it is being created and perform additional work on it. Here's an example (incomplete) patch illustrating the idea:

{{{
Index: django/contrib/admin/options.py
===================================================================
--- django/contrib/admin/options.py	(revision 8129)
+++ django/contrib/admin/options.py	(working copy)
@@ -324,7 +324,19 @@
     def get_formsets(self, request, obj=None):
         for inline in self.inline_instances:
             yield inline.get_formset(request, obj)
+    
+    def save_model_add(self, request, form, formsets):
+        new_object = form.save(commit=True)
+         
+        if formsets:
+            for formset in formsets:
+                # HACK: it seems like the parent obejct should be passed into
+                # a method of something, not just set as an attribute
+                formset.instance = new_object
+                formset.save()
+        
+        return new_object
+    
     def save_add(self, request, form, formsets, post_url_continue):
         """"""
         Saves the object in the ""add"" stage and returns an HttpResponseRedirect.
@@ -333,15 +345,9 @@
         """"""
         from django.contrib.admin.models import LogEntry, ADDITION
         opts = self.model._meta
-        new_object = form.save(commit=True)
-
-        if formsets:
-            for formset in formsets:
-                # HACK: it seems like the parent obejct should be passed into
-                # a method of something, not just set as an attribute
-                formset.instance = new_object
-                formset.save()
-
+        
+        new_object = self.save_model_add(request, form, formsets)
+        
         pk_value = new_object._get_pk_val()
         LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(self.model).id, 
             pk_value, force_unicode(new_object), ADDITION)
         msg = _('The %(name)s ""%(obj)s"" was added successfully.') % {'name': opts.verbose_name, 'obj': new_object}
@@ -371,7 +377,16 @@
                 post_url = '../../../'
             return HttpResponseRedirect(post_url)
     save_add = transaction.commit_on_success(save_add)
-
+    
+    def save_model_change(self, request, form, formsets):
+        new_object = form.save(commit=True)
+        
+        if formsets:
+            for formset in formsets:
+                formset.save()
+        
+        return new_object
+    
     def save_change(self, request, form, formsets=None):
         """"""
         Saves the object in the ""change"" stage and returns an HttpResponseRedirect.
@@ -382,13 +397,11 @@
         """"""
         from django.contrib.admin.models import LogEntry, CHANGE
         opts = self.model._meta
-        new_object = form.save(commit=True)
+        
+        new_object = self.save_model_change(request, form, formsets)
+        
         pk_value = new_object._get_pk_val()
-
-        if formsets:
-            for formset in formsets:
-                formset.save()
-
+        
         # Construct the change message.
         change_message = []
         if form.changed_data:
}}}
The above would allow ModelAdmin subclasses to make additional changes to the model by calling form.save(commit=False) instead. For the admin user described above, the subclass would look like this:
{{{
class ArticleAdmin(admin.ModelAdmin):
    
    def save_model_add(self, request, form, formsets):
        new_object = form.save(commit=False)
        new_object.created_by = request.user
        new_object.updated_by = request.user
        new_object.save()
        
        if formsets:
            for formset in formsets:
                formset.instance = new_object
                formset.save()
        
        return new_object
    
    def save_model_change(self, request, form, formsets):
        new_object = form.save(commit=False)
        new_object.updated_by = request.user
        new_object.save()
        
        if formsets:
            for formset in formsets:
                formset.save()
        
        return new_object
}}}
This is a big improvement, although I'm not too happy with the need to duplicate the formset logic. Maybe that should be split out in to a separate over-ridable method as well.
"		new	contrib.admin	dev					Unreviewed	1	1	1	1	0	0
