Automatically associating users with django models on save

I have spent way too much time trying to figure out how to automatically have created_by and modified_by be foreign keys to the current user, without mucking with every view and form. Every example I could find that told me how to do this, including django’s documentation itself, told me I had to customise every view and form, which I found to be rather un-DRY.

Thanks to stealing borrowing some code from django-audit-log (in order to be threadsafe) I figured out how to update these values on any model that has them, without any fiddling with forms or views. I might wrap this up in a little project, but for now, here’s the gist.

Note that you should set editable=False on your created_by and modified_by fields so that they will not otherwise show up in forms.

If anyone is really interested i could wrap this in a self-installable package.

10 thoughts on “Automatically associating users with django models on save

  1. I really like the spirit behind what you are doing.

    I’ve never done anything with Django Middleware before…
    What should my settings file look like?
    Where should I place this code?

  2. you put it in your settings.py – in a middleware thing like:


    MIDDLEWARE_CLASSES = (
        'django.middleware.common.CommonMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'whodid.middleware.WhodidMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        # Uncomment the next line for simple clickjacking protection:
        # 'django.middleware.clickjacking.XFrameOptionsMiddleware',
    )
    Thanks for the typo heads up!

  3. I created a folder in my django project called ‘middleware’ where I placed ‘middleware.py’.  I then opened ‘settings.py’ and added the following line: ‘middleware.middleware.WhodidMiddleware’ at the end of MIDDLEWARE_CLASSES.

    My ‘model.py’ looks like this:
    from django.db import modelsfrom django.contrib.auth.models import Userclass DateRecord(models.Model):    created_by = models.OneToOneField(User, related_name=”%(class)s_creator”, editable=False)    date_created = models.DateTimeField(auto_now_add=True)    modified_by = models.OneToOneField(User, related_name=”%(class)s_modifier”, editable=False)    last_modified = models.DateTimeField(auto_now=True)    def __unicode__(self):        return(            ‘Created By: ‘ + self.created_by.username +            ‘ Create On: ‘ + self.date_created.isoformat() +            ‘ Modified By: ‘ + self.modified_by.username +            ‘ Last Modified: ‘ + self.last_modified.isoformat()        )

    I’m getting the following error:
    AttributeError at /admin/testdate/daterecord/add/’str’ object has no attribute ‘creation_counter’Request Method:POSTRequest URL:http://127.0.0.1:8000/admin/testdate/daterecord/add/Django Version:1.3Exception Type:AttributeError 
    Any help you can offer is appreciated.

  4. The double signal seems to be because as written the code will both enter a created_by_id and modified_by_id on each INSERT. I did the following modification to change that:

    def mark_whodid(self, user, sender, instance, **kwargs):
    if not getattr(instance, ‘created_by_id’, None):
    instance.created_by = user
    elif hasattr(instance,’modified_by_id’): <—– Change if to elif
    instance.modified_by = user

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s