Подтвердить что ты не робот

Доступ к пользовательскому запросу в сигнале post_save

В моем проекте я сделал следующий сигнал 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 и сохранить его. Любая идея, как это сделать?

4b9b3361

Ответ 1

Невозможно выполнить. Текущий пользователь доступен только через запрос, который недоступен при использовании чисто функциональных возможностей модели. Доступ к пользователю в представлении каким-то образом.

Ответ 3

Я смог сделать это, проверив стек и посмотрев представление, а затем посмотрел на локальные переменные для представления, чтобы получить запрос. Он чувствует себя немного взломанным, но это сработало.

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

Ответ 4

Игнасио прав. Сигналы модели Django предназначены для уведомления других системных компонентов о событиях, связанных с экземплярами и их уважаемых данных, поэтому я полагаю, что вы не можете, скажем, получить доступ к данным запроса из сигнала модели post_save, если только эти данные запроса не были сохранены или связанных с экземпляром.

Я думаю, что есть много способов справиться с этим, начиная от худшего до лучшего, но я бы сказал, что это яркий пример для создания общих представлений на основе классов/функций, которые автоматически обрабатывают это для вас.

Пусть ваши представления, которые наследуют от CreateView, UpdateView или DeleteView, дополнительно наследуют от вашего класса AuditMixin, если они обрабатывают глаголы, которые работают с моделями, которые должны быть проверены. Затем AuditMixin может подключаться к представлениям, которые успешно создают\обновлять\удалять объекты и создавать запись в базе данных.

Делает совершенный смысл, очень чист, легко подключается и рождает счастливых пони. Оборотная сторона? Вы либо должны быть в выпуске выпущенной версии Django 1.3, либо вам придется потратить некоторое время, отвлекая на основе общих функций на основе функций и предоставляя новые для каждой операции аудита.

Ответ 5

Для отслеживания добавьте два атрибута к вашей модели (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))

Ответ 6

Вы также можете использовать 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

Ответ 7

Почему бы не добавить промежуточное ПО с чем-то вроде этого:

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

Ответ 8

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',
)

Ответ 9

Я предполагаю, что вы поняли бы это, но у меня была такая же проблема, и я понял, что все экземпляры, которые я создаю, имеют ссылку на пользователя, который создает их (это то, что вы ищете для)

Ответ 10

Вы можете сделать небольшой взлом, переопределив метод модели 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.