I have a model with a created_by field that is linked to the standard Django User model. I need to automatically populate this with the ID of the current User when the model is saved. I can't do this at the Admin layer, as most parts of the site will not use the built-in Admin. Can anyone advise on how I should go about this?
-
This post is pretty old. Is there an easy way to achieve this now? – Mar 14 '11 at 03:29
-
1@seanf What's the answer to this? – Mar 17 '11 at 13:10
-
4This [blog entry](http://www.b-list.org/weblog/2006/nov/02/django-tips-auto-populated-fields/) discusses exactly that. – Farinha May 14 '09 at 10:12
13 Answers
UPDATE 2020-01-02
⚠ The following answer was never updated to the latest Python and Django versions. Since writing this a few years ago packages have been released to solve this problem. Nowadays I highly recommend usingdjango-crum
which implements the same technique but has tests and is updated regularly: https://pypi.org/project/django-crum/
The least obstrusive way is to use a CurrentUserMiddleware
to store the current user in a thread local object:
current_user.py
from threading import local
_user = local()
class CurrentUserMiddleware(object):
def process_request(self, request):
_user.value = request.user
def get_current_user():
return _user.value
Now you only need to add this middleware to your MIDDLEWARE_CLASSES after the authentication middleware.
settings.py
MIDDLEWARE_CLASSES = (
...
'django.contrib.auth.middleware.AuthenticationMiddleware',
...
'current_user.CurrentUserMiddleware',
...
)
Your model can now use the get_current_user
function to access the user without having to pass the request object around.
models.py
from django.db import models
from current_user import get_current_user
class MyModel(models.Model):
created_by = models.ForeignKey('auth.User', default=get_current_user)
Hint:
If you are using Django CMS you do not even need to define your own CurrentUserMiddleware but can use cms.middleware.user.CurrentUserMiddleware
and the cms.utils.permissions.get_current_user
function to retrieve the current user.

- 7,337
- 1
- 23
- 29
-
2Seems thorough but in django 1.6.2 and Python 3.3.2 I receive the following error extract when I use south ```./manage.py schemamigration app --initial``` :```return _user.value AttributeError: '_thread._local' object has no attribute 'value'``` – raratiru Mar 22 '14 at 21:01
-
3That is right as the middleware is never called. For South (and probably a lot of other management commands) to work you need to catch the `AttributeError` in `get_current_user` and return `None`. – bikeshedder Mar 23 '14 at 18:58
-
Thank you! `Your model can now use the get_current_user function to access the user without having to pass the request object around.`. Is this the reason why using a middleware class is better practice than using [this snippet](https://code.djangoproject.com/wiki/CookBookNewformsAdminAndUser) or they can be considered as equivalent solutions? – raratiru Mar 24 '14 at 20:33
-
2@rara_tiru It depends on your needs. By using a thread local object and a middleware ANY object saved during the request with an authenticated user will fill the `created_by` field. The snippet only patches the admin. – bikeshedder Mar 25 '14 at 22:22
-
1For some reason this doesn't seem too clean to me, maybe just because I haven't gotten used to it yet, but I like the fact that this can be done. I was starting to get the impression that it was entirely impossible to reach arbitrary variables in a model function, in fact some people have outright stated as much. Thanks for sharing this. +1 – Teekin Nov 16 '14 at 17:44
If you want something that will work both in the admin and elsewhere, you should use a custom modelform. The basic idea is to override the __init__
method to take an extra parameter - request - and store it as an attribute of the form, then also override the save method to set the user id before saving to the database.
class MyModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
return super(MyModelForm, self).__init__(*args, **kwargs)
def save(self, *args, **kwargs):
kwargs['commit']=False
obj = super(MyModelForm, self).save(*args, **kwargs)
if self.request:
obj.user = self.request.user
obj.save()
return obj

- 5,053
- 7
- 43
- 69

- 588,541
- 66
- 880
- 895
-
2I'm unable to figure out how to make the admin to initialize MyModelForm with 'request' object. Is it even possible without modifying the contrib.admin code itself? – jholster Aug 23 '10 at 09:26
-
If using class based views it is very important to see Monster and Florentin's answers below, they are essential steps. – Mark Stahler Mar 06 '13 at 15:16
-
but what about having acces to it in `pre_save` of model? http://stackoverflow.com/questions/25305186/accessing-in-passing-to-django-models-signal-methods-like-pre-save-pre-delete – andilabs Aug 14 '14 at 10:02
-
5just to complete the answer. To be able to access `request` object in `__init__` , it must be passed when initialising your form in your view. Ex: `myform = MyForm(request.POST, request=request)` – vincebodi Jul 04 '15 at 10:06
-
way late...but how would this be modified for a modelformset in views.py? – OverflowingTheGlass Nov 07 '17 at 20:37
-
the syntax of `super(MyModelForm, self)` [changed in python3](https://docs.python.org/3/library/functions.html#super), just call `super()` – suhailvs Jul 07 '18 at 02:47
-
what is the problem with using something like: ```def save(self, user): obj = super().save(commit = False) obj.user = user obj.save() return stuff```. and then call it like `myform.save(request.user)` – suhailvs Jul 07 '18 at 04:03
-
Daniel's answer won't work directly for the admin because you need to pass in the request object. You might be able to do this by overriding the get_form
method in your ModelAdmin
class but it's probably easier to stay away from the form customisation and just override save_model
in your ModelAdmin
.
def save_model(self, request, obj, form, change):
"""When creating a new object, set the creator field.
"""
if not change:
obj.creator = request.user
obj.save()

- 7,062
- 1
- 35
- 33
-
Tim Fletcher, hello, i get an error if save_model is defined in admin.py when trying to create a new entry(model object of a class and that class has m2m field): "'MyClass' instance needs to have a primary key value before a many-to-many relationship can be used.". Can you advice what should be added? – ted Feb 24 '12 at 18:28
This whole approach bugged the heck out of me. I wanted to say it exactly once, so I implemented it in middleware. Just add WhodidMiddleware after your authentication middleware.
If your created_by & modified_by fields are set to editable = False
then you will not have to change any of your forms at all.
"""Add user created_by and modified_by foreign key refs to any model automatically.
Almost entirely taken from https://github.com/Atomidata/django-audit-log/blob/master/audit_log/middleware.py"""
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

- 313
- 2
- 8
-
The best answer. I ended up using getattr(instance, 'pk') to determine whether my models were being created. – Fábio Santos Nov 22 '12 at 16:45
-
2This code is not thread safe. If you are using a WSGI container that uses threads (e.g. Apache2 + mod_wsgi, uWSGI, etc.) you can end up storing the wrong user. – bikeshedder Feb 14 '14 at 18:22
-
This code is indeed not thread safe; probably better to avoid in production environments – acidjunk Jul 20 '16 at 14:20
-
@giantas this code is conceptually wrong. You need to use a `thread local` object for this to work properly. In doubt just use `django-crum` which works and is correct: https://pypi.org/project/django-crum/ – bikeshedder Jan 02 '20 at 10:09
here's how I do it with generic views:
class MyView(CreateView):
model = MyModel
def form_valid(self, form):
object = form.save(commit=False)
object.owner = self.request.user
object.save()
return super(MyView, self).form_valid(form)

- 7,963
- 17
- 80
- 151
-
1call `form.save()` again, not `object.save()`, in case there are commit-dependent customizations in the `save()` method (which anyway will call `save()` on the model instance) – trybik Mar 19 '18 at 11:13
If you are using class based views Daniel's answer needs more. Add the following to ensure that the request object is available for us in your ModelForm object
class BaseCreateView(CreateView):
def get_form_kwargs(self):
"""
Returns the keyword arguments for instanciating the form.
"""
kwargs = {'initial': self.get_initial()}
if self.request.method in ('POST', 'PUT'):
kwargs.update({
'data': self.request.POST,
'files': self.request.FILES,
'request': self.request})
return kwargs
Also, as already mentioned, you need to return the obj at the end of ModelForm.save()

- 4,499
- 30
- 38

- 71
- 1
- 1
what is the problem with using something like:
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
exclude = ['created_by']
def save(self, user):
obj = super().save(commit = False)
obj.created_by = user
obj.save()
return obj
Now call it like myform.save(request.user)
in the views.
here is ModelForm's save function, which has only a commit
parameter.

- 20,182
- 14
- 100
- 98
For future references, best solution I found about this subject:
https://pypi.python.org/pypi/django-crum/0.6.1
This library consist of some middleware. After setting up this libary, simply override the save method of model and do the following,
from crum import get_current_user
def save(self, *args, **kwargs):
user = get_current_user()
if not self.pk:
self.created_by = user
else:
self.changed_by = user
super(Foomodel, self).save(*args, **kwargs)
if you create and abstract model and inherit from it for all your model, you get your auto populated created_by and changed_by fields.

- 1,590
- 1
- 14
- 36
Based on bikeshedder's answer, I found a solution since his did not actually work for me.
app/middleware/current_user.py
from threading import local _user = local() class CurrentUserMiddleware(object): def __init__(self, get_response): self.get_response = get_response def __call__(self, request): _user.value = request.user return self.get_response(request) def get_current_user(): return _user.value
settings.py
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'common.middleware.current_user.CurrentUserMiddleware', ]
model.py
from common.middleware import current_user created_by = models.ForeignKey(User, blank=False, related_name='created_by', editable=False, default=current_user.get_current_user)
I'm using python 3.5 and django 1.11.3
-
while running ./manage.py migrate, I get the following error: `AttributeError: '_thread._local' object has no attribute 'value'` which makes sense as the request middle ware is not loaded during migration. Or did I missing something here? Could you please clarify? – radiopassive Apr 04 '19 at 10:11
From the Django documentation Models and request.user:
" To track the user that created an object using a CreateView, you can use a custom ModelForm. In the view, ensure that you don’t include [the user field] in the list of fields to edit, and override form_valid() to add the user:
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic.edit import CreateView
from myapp.models import Author
class AuthorCreate(LoginRequiredMixin, CreateView):
model = Author
fields = ['name']
def form_valid(self, form):
form.instance.created_by = self.request.user
return super().form_valid(form)

- 1,250
- 13
- 25
I don't believe Daniel's answer is the best there is since it changes the default behaviour of a model form by always saving the object.
The code I would use:
forms.py
from django import forms
class MyModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(MyModelForm, self).__init__(*args, **kwargs)
def save(self, commit=True):
obj = super(MyModelForm, self).save(commit=False)
if obj.created_by_id is None:
obj.created_by = self.user
if commit:
obj.save()
return obj

- 2,482
- 1
- 13
- 10
The 'save' method from forms.ModelForm returns the saved instanced.
You should add one last line to MyModelForm:
...
return obj
This change is necessary if you are using create_object or update_object generic views.
They use the saved object to do the redirect.

- 651
- 8
- 8
Note sure if you were looking for this, but adding the following
user = models.ForeignKey('auth.User')
to a model will work to add the user id to the model.
In the following, each hierarchy belongs to a user.
class Hierarchy(models.Model):
user = models.ForeignKey('auth.User')
name = models.CharField(max_length=200)
desc = models.CharField(max_length=1500)

- 11