1

My goal is to build an abstract BaseModel model which will have the following attributes :

  1. date_created
  2. created_by - linked to the USER model
  3. date_updated
  4. updated_by - linked to the USER model

For the date_created and the date_updated, it is ok, I can use the auto_now and auto_now_add parameters.

For the created_by and updated_by, I could not find how to do it automatically in a thread-safe manner. Of course, I can do that for each model on the view, but I want to avoid to repeat myself and do it in once one place.

So I was thinking like accessing the session request or the request object himself on my abstract model...

any idea?

storm_buster
  • 7,362
  • 18
  • 53
  • 75

1 Answers1

1

You don't have access to the request (and therefore the current user) from your model. This is by design in Django.

The more straightforward approaches are less DRY but more explicit:

  1. Create a reusable ModelForm that automatically saves the user to the model when the form is saved
  2. Create reusable class based View that automatically saves the user to your model
  3. Create or a reuseable ModelAdmin that automatically saves the user to the model (or this answer)

But if you want truly transparent saving of creator/modifier, that is more DRY but less explicit, you can:

  1. Write some middleware that transparently saves the user information to the model on save using signals
  2. Alternatively you could use a threadlocal to set the current user via a more simple piece of middleware and then access that threadlocal again in your models save() method. You need to be careful with threadlocals

This is the first of the more transparent options:

from django.db.models import signals
from django.utils.functional import curry

class WhodidMiddleware(object):
    def process_request(self, request):
        if not request.method in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
            if hasattr(request, 'user') and request.user.is_authenticated():
                user = request.user
            else:
                user = None

            mark_whodid = curry(self.mark_whodid, user)
            signals.pre_save.connect(mark_whodid,  dispatch_uid = (self.__class__, request,), weak = False)

    def process_response(self, request, response):
        signals.pre_save.disconnect(dispatch_uid =  (self.__class__, request,))
        return response

    def mark_whodid(self, user, sender, instance, **kwargs):
        if 'created_by' in instance._meta.fields and not instance.created_by:
            instance.created_by = user
        if 'modified_by' in instance._meta.fields:
            instance.modified_by = user
Community
  • 1
  • 1
Timmy O'Mahony
  • 53,000
  • 18
  • 155
  • 177
  • 1
    Thank for you answer, Have you used theses strategies in a real project?What do you recommend? What the pros and con for each method? In which cases, the strategies will not work? – storm_buster Feb 04 '14 at 23:19
  • Take care, we used this in production and it's not thread-safe. I don't know why but users tend to get mixed up... – Ron Jan 04 '19 at 14:48