Ответ 1
Невозможно выполнить. Текущий пользователь доступен только через запрос, который недоступен при использовании чисто функциональных возможностей модели. Доступ к пользователю в представлении каким-то образом.
В моем проекте я сделал следующий сигнал post_save.
from django.db.models.signals import post_save
from django.contrib.auth.models import User
# CORE - SIGNALS
# Core Signals will operate based on post
def after_save_handler_attr_audit_obj(sender, **kwargs):
print User.get_profile()
if hasattr(kwargs['instance'], 'audit_obj'):
if kwargs['created']:
kwargs['instance'].audit_obj.create(operation="INSERT", operation_by=**USER.ID**).save()
else:
kwargs['instance'].audit_obj.create(operation="UPDATE").save()
# Connect the handler with the post save signal - Django 1.2
post_save.connect(after_save_handler_attr_audit_obj, dispatch_uid="core.models.audit.new")
В столбце operation_by я хочу получить user_id и сохранить его. Любая идея, как это сделать?
Невозможно выполнить. Текущий пользователь доступен только через запрос, который недоступен при использовании чисто функциональных возможностей модели. Доступ к пользователю в представлении каким-то образом.
Посмотрите django-contrib-requestprovider, возможно, вы можете его использовать.
Я смог сделать это, проверив стек и посмотрев представление, а затем посмотрел на локальные переменные для представления, чтобы получить запрос. Он чувствует себя немного взломанным, но это сработало.
import inspect, os
@receiver(post_save, sender=MyModel)
def get_user_in_signal(sender, **kwargs):
for entry in reversed(inspect.stack()):
if os.path.dirname(__file__) + '/views.py' == entry[1]:
try:
user = entry[0].f_locals['request'].user
except:
user = None
break
if user:
# do stuff with the user variable
Игнасио прав. Сигналы модели Django предназначены для уведомления других системных компонентов о событиях, связанных с экземплярами и их уважаемых данных, поэтому я полагаю, что вы не можете, скажем, получить доступ к данным запроса из сигнала модели post_save
, если только эти данные запроса не были сохранены или связанных с экземпляром.
Я думаю, что есть много способов справиться с этим, начиная от худшего до лучшего, но я бы сказал, что это яркий пример для создания общих представлений на основе классов/функций, которые автоматически обрабатывают это для вас.
Пусть ваши представления, которые наследуют от CreateView
, UpdateView
или DeleteView
, дополнительно наследуют от вашего класса AuditMixin
, если они обрабатывают глаголы, которые работают с моделями, которые должны быть проверены. Затем AuditMixin
может подключаться к представлениям, которые успешно создают\обновлять\удалять объекты и создавать запись в базе данных.
Делает совершенный смысл, очень чист, легко подключается и рождает счастливых пони. Оборотная сторона? Вы либо должны быть в выпуске выпущенной версии Django 1.3, либо вам придется потратить некоторое время, отвлекая на основе общих функций на основе функций и предоставляя новые для каждой операции аудита.
Для отслеживания добавьте два атрибута к вашей модели (created_by
и updated_by
), в "updated_by" сохраните последнего пользователя, который изменил запись. Затем в вашем сигнале у вас есть пользователь:
models.py:
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
created_by = models. (max_length=100)
updated_by = models. (max_length=100)
views.py
p = Question.objects.get(pk=1)
p.question_text = 'some new text'
p.updated_by = request.user
p.save()
signals.py
@receiver(pre_save, sender=Question)
def do_something(sender, instance, **kwargs):
try:
obj = Question.objects.get(pk=instance.pk)
except sender.DoesNotExist:
pass
else:
if not obj.user == instance.user: # Field has changed
# do something
print('change: user, old=%s new=%s' % (obj.user, instance.user))
Вы также можете использовать django-reversion для этой цели, например
from reversion.signals import post_revision_commit
import reversion
@receiver(post_save)
def post_revision_commit(sender, **kwargs):
if reversion.is_active():
print(reversion.get_user())
Узнайте больше об их API https://django-reversion.readthedocs.io/en/stable/api.html#revision-api
Почему бы не добавить промежуточное ПО с чем-то вроде этого:
class RequestMiddleware(object):
thread_local = threading.local()
def process_request(self, request):
RequestMiddleware.thread_local.current_user = request.user
а затем в вашем коде (особенно в сигнале в этой теме):
thread_local = RequestMiddleware.thread_local
if hasattr(thread_local, 'current_user'):
user = thread_local.current_user
else:
user = None
context_processors.py
from django.core.cache import cache
def global_variables(request):
cache.set('user', request.user)
----------------------------------
in you model
from django.db.models.signals import pre_delete
from django.dispatch import receiver
from django.core.cache import cache
from news.models import News
@receiver(pre_delete, sender=News)
def news_delete(sender, instance, **kwargs):
user = cache.get('user')
in settings.py
TEMPLATE_CONTEXT_PROCESSORS = (
'web.context_processors.global_variables',
)
Я предполагаю, что вы поняли бы это, но у меня была такая же проблема, и я понял, что все экземпляры, которые я создаю, имеют ссылку на пользователя, который создает их (это то, что вы ищете для)
Вы можете сделать небольшой взлом, переопределив метод модели save()
и установив пользователя для сохраненного экземпляра в качестве дополнительного параметра. Чтобы получить пользователя, я использовал get_current_authenticated_user()
из django_currentuser.middleware.ThreadLocalUserMiddleware
(см. Https://pypi.org/project/django-currentuser/).
В ваших models.py
:
from django_currentuser.middleware import get_current_authenticated_user
class YourModel(models.Model):
...
...
def save(self, *args, **kwargs):
# Hack to pass the user to post save signal.
self.current_authenticated_user = get_current_authenticated_user()
super(YourModel, self).save(*args, **kwargs)
В твоих signals.py
:
@receiver(post_save, sender=YourModel)
def your_model_saved(sender, instance, **kwargs):
user = getattr(instance, 'current_authenticated_user', None)
PS: не забудьте добавить 'django_currentuser.middleware.ThreadLocalUserMiddleware'
к вашему MIDDLEWARE_CLASSES
.