40π
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.
36π
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
- [Django]-Django class-based view: How do I pass additional parameters to the as_view method?
- [Django]-Class views in Django
- [Django]-Get distinct values of Queryset by field
27π
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()
- [Django]-Django delete FileField
- [Django]-Django template can't loop defaultdict
- [Django]-How to remove all relations from manytomany?
15π
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
- [Django]-Target WSGI script cannot be loaded as Python module
- [Django]-How to update multiple fields of a django model instance?
- [Django]-The STATICFILES_DIRS setting should not contain the STATIC_ROOT setting
10π
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)
- [Django]-Foreign key from one app into another in Django
- [Django]-Django query filter with variable column
- [Django]-Django multiple template inheritance β is this the right style?
7π
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()
- [Django]-Django FileField: How to return filename only (in template)
- [Django]-Django REST Framework: adding additional field to ModelSerializer
- [Django]-Django URLs TypeError: view must be a callable or a list/tuple in the case of include()
4π
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.
- [Django]-How do I return JSON without using a template in Django?
- [Django]-How do I render jinja2 output to a file in Python instead of a Browser
- [Django]-Django F() division β How to avoid rounding off
3π
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.
- [Django]-Django DB Settings 'Improperly Configured' Error
- [Django]-Accepting email address as username in Django
- [Django]-Django check if a related object exists error: RelatedObjectDoesNotExist
3π
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
- [Django]-Django models ForeignKey on_delete attribute: full meaning?
- [Django]-Paginating the results of a Django forms POST request
- [Django]-Foreign key from one app into another in Django
2π
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)
- [Django]-Django: TemplateSyntaxError: Could not parse the remainder
- [Django]-DRY way to add created/modified by and time
- [Django]-Django: Converting an entire set of a Model's objects into a single dictionary
1π
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.
- [Django]-Suppress "?next=blah" behavior in django's login_required decorator
- [Django]-How do I make many-to-many field optional in Django?
- [Django]-How to get the label of a choice in a Django forms ChoiceField?
1π
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
- [Django]-How to get value from form field in django framework?
- [Django]-How to override and extend basic Django admin templates?
- [Django]-How can I avoid "Using selector: EpollSelector" log message in Django?
-5π
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)
- [Django]-Dropdown in Django Model
- [Django]-Request.user returns a SimpleLazyObject, how do I "wake" it?
- [Django]-Save() prohibited to prevent data loss due to unsaved related object