Django: Получить текущего пользователя в сохранении модели
Я хочу получить текущего зарегистрированного пользователя (request.user
) в методе save
models.py. Я хочу проверить роль пользователя и посмотреть, сможет ли он выполнять некоторые операции в зависимости от его роли.
models.py:
class TimeSheet(models.Model):
check_in_time = models.TimeField()
check_out_time = models.TimeField()
class Tasks(models.Model):
time_sheet = models.ForeignKey(TimeSheet)
project = models.ForeignKey(Project)
start_time = models.TimeField()
end_time = models.TimeField()
def save(self, *args,**kwargs):
project = SpentTime.objects.get(project__project__id = self.project.id)
start = datetime.datetime.strptime(str(self.start_time), '%H:%M:%S')
end = datetime.datetime.strptime(str(self.end_time), '%H:%M:%S')
time = float("{0:.2f}".format((end - start).seconds/3600.0))
if common.isDesigner(request.user):
SpentTime.objects.filter(project__project__id = self.project.id).update(design = float(project.design) + time)
if common.isDeveloper(request.user):
SpentTime.objects.filter(project__project__id = self.project.id).update(develop = float(project.develop) + time)
super(Tasks, self).save(*args, **kwargs)
Здесь модель Tasks
используется как встроенная в модели Timesheet
. Я хочу проверить роль текущего пользователя, вошедшего в систему, и обновить другую модель в зависимости от роли пользователя. Здесь мне нужно request.user
, чтобы проверить роль текущего пользователя. Я не использую какие-либо формы или шаблоны и полностью использую администратора Django. Есть ли способ получить request.user
в методе save
или проверить и обновить значения в другой модели в admin.py?
Ответы
Ответ 1
Вы можете решить эту проблему под другим углом. Вместо изменения метода сохранения моделей вы должны переопределить метод AdminSites save_model
. Там у вас будет объект запроса, и вы сможете получить доступ к зарегистрированным данным пользователя, как вы уже указали.
Ознакомьтесь с этой главой документации: документация Django ModelAdmin save_model
Ответ 2
Я нашел способ сделать это, но это связано с объявлением MiddleWare. Создайте файл с именем get_username.py
внутри вашего приложения с этим контентом:
from threading import current_thread
_requests = {}
def get_username():
t = current_thread()
if t not in _requests:
return None
return _requests[t]
class RequestMiddleware(object):
def process_request(self, request):
_requests[current_thread()] = request
Отредактируйте свой settings.py
и добавьте его в MIDDLEWARE_CLASSES
:
MIDDLEWARE_CLASSES = (
...
'yourapp.get_username.RequestMiddleware',
)
Теперь, в вашем методе save()
, вы можете получить текущее имя пользователя следующим образом:
from get_username import get_username
...
def save(self, *args, **kwargs):
req = get_username()
print "Your username is: %s" % (req.user)
Ответ 3
Я не думаю, что метод save_model override является лучшим вариантом. Предположим, например, что вы хотите сохранить информацию о пользователе или проверить модель на основе информации о пользователе, и что save() не приходит из представления или самого админита.
То, что люди спрашивают, это конструкции, подобные тем, что:
def save(..)
self.user = current_user()
или
def save(..)
user = current_user()
if user.group == 'XPTO':
error('This user cannot edit this record')
Лучший подход, который я нашел до сих пор:
https://bitbucket.org/q/django-current-user/overview
Ответ 4
Решение, предложенное @nKn, является хорошей отправной точкой, но когда я попытался реализовать его сегодня, я столкнулся с двумя проблемами:
- В текущей версии Django промежуточное программное обеспечение, созданное как обычный объект, не работает.
- Юнит-тесты терпят неудачу (так как они обычно выполняются в одном потоке, поэтому ваш "запрос" может остаться между двумя последовательными тестами, если первый тест имеет HTTP-запрос, а второй - нет).
Вот мой обновленный код промежуточного программного обеспечения, который работает с Django 1.10 и не нарушает юнит-тесты:
from threading import current_thread
from django.utils.deprecation import MiddlewareMixin
_requests = {}
def current_request():
return _requests.get(current_thread().ident, None)
class RequestMiddleware(MiddlewareMixin):
def process_request(self, request):
_requests[current_thread().ident] = request
def process_response(self, request, response):
# when response is ready, request should be flushed
_requests.pop(current_thread().ident, None)
return response
def process_exception(self, request, exception):
# if an exception has happened, request should be flushed too
_requests.pop(current_thread().ident, None)
Ответ 5
Правильный способ - использовать threading.local
вместо словаря с threading.current_thread
, так как это приведет к утечке памяти, поскольку старые значения будут оставаться там до тех пор, пока приложение работает:
import threading
request_local = threading.local()
def get_request():
return getattr(request_local, 'request', None)
class RequestMiddleware():
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
request_local.request = request
return self.get_response(request)
def process_exception(self, request, exception):
request_local.request = None
def process_template_response(self, request, response):
request_local.request = None
return response
Если вы хотите получить доступ к пользователю вместо запроса, вы можете get_request().user
или сохранить пользователя вместо запроса на __call__
Ответ 6
Вы пытались использовать следующую библиотеку:
https://pypi.org/project/django-currentuser/
Фрагмент того, как он используется на веб-сайте:
from django_currentuser.middleware import (get_current_user, get_current_authenticated_user)
# As model field:
from django_currentuser.db.models import CurrentUserField
class Foo(models.Model):
created_by = CurrentUserField()