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

发布于 2024-10-13 02:09:46 字数 775 浏览 3 评论 0 原文

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

I have done the below post_save signal in my project.

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")

The operation_by column, I want to get the user_id and store it. Any idea how can do that?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

鸠书 2024-10-20 02:09:46

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

Can't be done. The current user is only available via the request, which is not available when using purely model functionality. Access the user in the view somehow.

感性不性感 2024-10-20 02:09:46

您可以借助中间件来做到这一点。在您的应用中创建 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")

You can do that with the help of middleware. Create get_request.py in your app. Then

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)

Then add this middleware to your settings:

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

Then add import to your signals:

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")
初见终念 2024-10-20 02:09:46

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

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

I was able to do it by inspecting the stack and looking for the view then looking at the local variables for the view to get the request. It feels like a bit of a hack, but it worked.

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
情定在深秋 2024-10-20 02:09:46

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

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

让继承自 CreateViewUpdateViewDeleteView 的视图另外继承自 AuditMixin 类(如果它们处理以下动词)对需要审核的模型进行操作。然后,AuditMixin 可以挂钩到成功创建\更新\删除对象并在数据库中创建条目的视图。

非常有意义,非常干净,易于插拔,并孕育出快乐的小马。反面?您要么必须使用即将发布的 Django 1.3 版本,要么必须花一些时间调整基于函数的通用视图并为每个审核操作提供新的视图。

Ignacio is right. Django's model signals are intended to notify other system components about events associated with instances and their respected data, so I guess it's valid that you cannot, say, access request data from a model post_save signal, unless that request data was stored on or associated with the instance.

I guess there are lots of ways to handle it, ranging from worse to better, but I'd say this is a prime example for creating class-based/function-based generic views that will automatically handle this for you.

Have your views that inherit from CreateView, UpdateView or DeleteView additionally inherit from your AuditMixin class if they handle verbs that operate on models that need to be audited. The AuditMixin can then hook into the views that successfully create\update\delete objects and create an entry in the database.

Makes perfect sense, very clean, easily pluggable and gives birth to happy ponies. Flipside? You'll either have to be on the soon-to-be-released Django 1.3 release or you'll have to spend some time fiddlebending the function-based generic views and providing new ones for each auditing operation.

半暖夏伤 2024-10-20 02:09:46

为什么不添加一个中间件,如下所示:

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

Why not adding a middleware with something like this :

class RequestMiddleware(object):

    thread_local = threading.local()

    def process_request(self, request):
        RequestMiddleware.thread_local.current_user = request.user

and later in your code (specially in a signal in that topic) :

thread_local = RequestMiddleware.thread_local
if hasattr(thread_local, 'current_user'):
    user = thread_local.current_user
else:
    user = None
笑咖 2024-10-20 02:09:46

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

For traceability add two attributes to your Model(created_by and updated_by), in "updated_by" save the last user who modified the record. Then in your signal you have the user:

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))
枕头说它不想醒 2024-10-20 02:09:46

您也可以使用 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

You could also use django-reversion for this purpose, e.g.

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())

Read more on their API https://django-reversion.readthedocs.io/en/stable/api.html#revision-api

倚栏听风 2024-10-20 02:09:46

您可以通过重写模型 save() 方法并将保存的实例上的用户设置为附加参数来进行小修改。为了获取用户,我使用了 django_currentuser.middleware.ThreadLocalUserMiddleware 中的 get_current_authenticated_user() (参见 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

You can do a small hack by overriding you model save() method and setting the user on the saved instance as additional parameter. To get the user I used get_current_authenticated_user() from django_currentuser.middleware.ThreadLocalUserMiddleware (see https://pypi.org/project/django-currentuser/).

In your 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)

In your signals.py:

@receiver(post_save, sender=YourModel)
def your_model_saved(sender, instance, **kwargs):
    user = getattr(instance, 'current_authenticated_user', None)

PS: Don't forget to add 'django_currentuser.middleware.ThreadLocalUserMiddleware' to your MIDDLEWARE_CLASSES.

等数载,海棠开 2024-10-20 02:09:46

我想您应该已经弄清楚了这一点,但我遇到了同样的问题,并且我意识到我创建的所有实例都引用了创建它们的用户(这就是您正在寻找的)

I imagine you would have figured this out, but I had the same problem and I realised that all the instances I create had a reference to the user that creates them (which is what you are looking for)

迷荒 2024-10-20 02:09:46

我猜有可能。

在models.py中

class _M(models.Model):
    user = models.ForeignKey(...)

在views.py中

def _f(request):
    _M.objects.create(user=request.user)

在signals.py中

@receiver(post_save, sender=_M)
def _p(sender, instance, created, **kwargs):
   user = instance.user

否?

it's possible i guess.

in models.py

class _M(models.Model):
    user = models.ForeignKey(...)

in views.py

def _f(request):
    _M.objects.create(user=request.user)

in signals.py

@receiver(post_save, sender=_M)
def _p(sender, instance, created, **kwargs):
   user = instance.user

No ?

不一样的天空 2024-10-20 02:09:46

可以通过检查从帧记录中获取请求对象。

import inspect

request = [
    frame_record[0].f_locals["request"]
    for frame_record in inspect.stack()
    if frame_record[3] == "get_response"
][0]

Request object can be obtained from frame record by inspecting.

import inspect

request = [
    frame_record[0].f_locals["request"]
    for frame_record in inspect.stack()
    if frame_record[3] == "get_response"
][0]
舟遥客 2024-10-20 02:09:46
def get_requested_user():
    import inspect
    for frame_record in inspect.stack():
        if frame_record[3] == 'get_response':
            request = frame_record[0].f_locals['request']
            return request.user
    else:
        return None
def get_requested_user():
    import inspect
    for frame_record in inspect.stack():
        if frame_record[3] == 'get_response':
            request = frame_record[0].f_locals['request']
            return request.user
    else:
        return None
狠疯拽 2024-10-20 02:09:46
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',
)
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',
)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文