Code

Opened 6 years ago

Closed 6 years ago

#8005 closed (duplicate)

Make it easier to alter a model saved by the admin (e.g. add the user that created or updated it)

Reported by: simon Owned by: nobody
Component: contrib.admin Version: master
Severity: Keywords:
Cc: Triage Stage: Design decision needed
Has patch: yes Needs documentation: yes
Needs tests: yes Patch needs improvement: yes
Easy pickings: UI/UX:

Description (last modified by simon)

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: http://code.djangoproject.com/attachment/ticket/8005/8005.1.diff

This 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.

Attachments (3)

8005.1.diff (2.9 KB) - added by simon 6 years ago.
8005.2.diff (3.0 KB) - added by simon 6 years ago.
Improved patch; refactored formset boilerplate in to a separate method
8005.3.diff (1.7 KB) - added by simon 6 years ago.
Patch illustrating different approach, using pre_save and post_save hooks

Download all attachments as: .zip

Change History (11)

Changed 6 years ago by simon

comment:1 Changed 6 years ago by simon

  • Description modified (diff)

Changed 6 years ago by simon

Improved patch; refactored formset boilerplate in to a separate method

comment:2 Changed 6 years ago by simon

http://code.djangoproject.com/attachment/ticket/8005/8005.2.diff is a bit better - it lets you skip the formset boilerplate, so now the subclass looks 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()
        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()
        return new_object

Changed 6 years ago by simon

Patch illustrating different approach, using pre_save and post_save hooks

comment:3 Changed 6 years ago by simon

I've added a third option, based on feedback from Julien Phalip: http://code.djangoproject.com/attachment/ticket/8005/8005.2.diff

Now the subclass looks like this:

class ArticleAdmin(admin.ModelAdmin):
    
    def pre_save(self, request, instance, is_add):
        if is_add: 
            instance.created_by = request.user 
        instance.updated_by = request.user

Mailing list thread is here: http://groups.google.com/group/django-developers/browse_thread/thread/620b4dffa8a7f58f

comment:4 Changed 6 years ago by simon

Previous comment linked to the wrong patch; should have linked to http://code.djangoproject.com/attachment/ticket/8005/8005.3.diff

comment:5 Changed 6 years ago by julien

I really think the pre_save and post_save hooks are the best approach here. Another example of what you could do with it:

class ArticleAdmin(admin.ModelAdmin):

     def post_save(self, request, instance, is_add):
         if is_add:
            mail_admins("New article created", "A new article created by %s in the admin. Check it out: %s" % (instance.created_by, instance.get_absolute_url()) )

comment:6 Changed 6 years ago by julien

Some other approaches have been proposed in #6406. Not sure which ticket should be considered most in this regard.

comment:7 Changed 6 years ago by ericholscher

  • Triage Stage changed from Unreviewed to Design decision needed

comment:8 Changed 6 years ago by jacob

  • Resolution set to duplicate
  • Status changed from new to closed

This is dancing around the same problems as #6406 and #8005. For sanity's sake, let's make #6002 the master here.

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
as The resolution will be set. Next status will be 'closed'
The resolution will be deleted. Next status will be 'new'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.