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

Mo *_*abi 34 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")
Run Code Online (Sandbox Code Playgroud)

在operation_by列中,我想获取user_id并存储它.知道如何做到这一点?

Ign*_*ams 25

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


Pau*_*ulR 14

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

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
Run Code Online (Sandbox Code Playgroud)

  • 非常hacky的解决方案,否决,因为这是一个非常糟糕的做法 (6认同)
  • 这不是“正确的”,但效果很好,所以我认为这是正确的答案。 (2认同)
  • 有时它可能会起作用,但这是一种极端的黑客行为,不适合使用:否决。 (2认同)

小智 13

您可以借助中间件来做到这一点。在您的应用程序中创建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)
Run Code Online (Sandbox Code Playgroud)

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

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

Run Code Online (Sandbox Code Playgroud)

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

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")
Run Code Online (Sandbox Code Playgroud)


Fil*_*vić 7

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

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

让你的观点,即继承CreateView,UpdateViewDeleteView另外从你的继承AuditMixin类,如果他们处理的是对需要审核模式操作的动词.然后,AuditMixin可以挂钩到成功创建\ update\delete对象的视图,并在数据库中创建一个条目.

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


fir*_*631 6

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

class RequestMiddleware(object):

    thread_local = threading.local()

    def process_request(self, request):
        RequestMiddleware.thread_local.current_user = request.user
Run Code Online (Sandbox Code Playgroud)

稍后在您的代码中(特别是在该主题中的信号中):

thread_local = RequestMiddleware.thread_local
if hasattr(thread_local, 'current_user'):
    user = thread_local.current_user
else:
    user = None
Run Code Online (Sandbox Code Playgroud)


小智 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)
Run Code Online (Sandbox Code Playgroud)

views.py

    p = Question.objects.get(pk=1)
    p.question_text = 'some new text'
    p.updated_by = request.user
    p.save()
Run Code Online (Sandbox Code Playgroud)

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))
Run Code Online (Sandbox Code Playgroud)

  • 这非常容易出现竞争条件。对于很多用例来说这可能没问题,但应该提到它。 (2认同)

小智 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())
Run Code Online (Sandbox Code Playgroud)

阅读更多关于他们的API https://django-reversion.readthedocs.io/en/stable/api.html#revision-api