在post_save信号中访问用户的请求

时间:2011-01-17 18:10:38

标签: python django signals

我在我的项目中完成了以下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并存储它。知道如何做到这一点?

10 个答案:

答案 0 :(得分:20)

无法完成。当前用户仅可通过请求获得,这在使用纯模型功能时不可用。以某种方式访问​​视图中的用户。

答案 1 :(得分:10)

我能够通过检查堆栈并查找视图,然后查看视图的局部变量来获取请求。这感觉有点像黑客,但它确实有效。

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

答案 2 :(得分:7)

伊格纳西奥是对的。 Django的模型信号旨在通知其他系统组件有关与实例相关的事件及其受尊重的数据,因此我认为您无法从模型post_save信号访问请求数据,除非该请求数据已存储在实例上或与实例相关联。

我想有很多方法可以处理它,从更糟到更好,但我会说这是一个 prime 示例,用于创建基于类的/基于函数的通用视图自动为您处理。

CreateViewUpdateViewDeleteView继承的视图如果处理对需要审核的模型进行操作的动词,则会继续从AuditMixin类继承。然后AuditMixin可以挂钩到成功创建\ update \ delete对象的视图中,并在数据库中创建一个条目。

具有完美的感觉,非常干净,易于插拔,并生出快乐的小马。另一面?您要么必须处于即将发布的Django 1.3版本中,要么您将不得不花费一些时间来完成基于函数的通用视图并为每个审计操作提供新的视图。

答案 3 :(得分:5)

为了追溯,为模型添加两个属性(created_byupdated_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))

答案 4 :(得分:5)

你也可以为此目的使用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

答案 5 :(得分:3)

为什么不添加类似这样的中间件:

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

答案 6 :(得分:2)

您可以在中间件的帮助下做到这一点。在您的应用中创建 get_request.py。那么

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)

然后将此中间件添加到您的设置中:

MIDDLEWARE = [
    ....
    '<your_app>.get_request.RequestMiddleware',
]

然后将导入添加到您的信号中:

from django.db.models.signals import post_save
from django.contrib.auth.models import User
from <your_app>.get_request import current_request

# CORE - SIGNALS
# Core Signals will operate based on post

def after_save_handler_attr_audit_obj(sender, **kwargs):
    print(Current User, current_request().user)
    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")

答案 7 :(得分:0)

我想你会想到这一点,但我遇到了同样的问题,我意识到我创建的所有实例都有对创建它们的用户的引用(这就是你在寻找的对)

答案 8 :(得分:0)

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 :(得分:-2)

您可以通过重写模型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