【问题标题】:How to safely access request object in Django models如何安全地访问 Django 模型中的请求对象
【发布时间】:2019-02-12 15:38:28
【问题描述】:

我想做的事:

我正在尝试访问我的 django 模型中的请求对象,以便我可以使用 request.user 获取当前登录的用户。

我尝试过的:

我在this 网站上发现了一个黑客。但是 cmets 中有人指出在生产时不要这样做。

我还尝试覆盖模型的__init__ 方法,就像this 帖子中提到的那样。但我得到了一个 AttributeError: 'RelatedManager' object has no attribute 'request'

Models.py:

class TestManager(models.Manager):
    def user_test(self):
        return self.filter(user=self.request.user, viewed=False)

class Test(models.Model):

    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request', None)
        super(Test, self).__init__(*args, **kwargs)

    user = models.ForeignKey(User, related_name='test') 
    viewed = models.BooleanField(default=False)
    objects = TestManager()

【问题讨论】:

  • 通常模型层应该永远有权访问request对象。这个想法是模型层仅对业务逻辑进行建模,视图用于实现“用例”。所以这样做实际上会打破“层”。 Models 及其关系是请求感知的,因为模型本身不是通过请求处理的(例如通过命令)。
  • 此外,引用的文章实际上是非常有问题的:如果应用程序处理并发请求,这可能会变得非常糟糕,因为请求是通过中间件的最新请求,而不是那个由控制流处理。

标签: django django-models django-request


【解决方案1】:

我尝试访问我的 Django 模型中的请求对象,以便我可以使用 request.user 获取当前登录的用户。

一个问题是模型本身在请求的上下文中使用。例如,一个经常定义custom commands 来做记账,或者一个可以定义一个API,例如用户在场。 Django 方法的想法是模型应该是请求感知的。模型定义“业务逻辑”层:模型定义实体以及它们如何交互。如果不尊重这些层,就会使应用程序容易出现很多问题。

您引用的博客旨在创建他们所谓的全局状态(这是一个严重的anti-patten):当视图进行调用时,您将请求保存在中间件中,这样然后,您可以在模型层中获取该对象。这种方法存在一些问题:首先,正如已经说过的,并非所有用例都是视图,因此并非所有用例都通过中间件。因此,该属性在获取时可能存在。

此外,不保证请求对象确实是视图的请求对象。例如,我们可能将模型层与不通过中间件的命令一起使用,在这种情况下,我们应该使用先前的视图请求(因此可能与不同的用户一起使用)。如果服务器同时处理多个请求,视图也可能会看到几纳秒后到达的请求,从而再次获取错误的用户。身份验证中间件也可能是有条件的,因此并非所有请求都具有user 属性。简而言之,有足够多的场景可能会失败,并且结果可能很严重:人们查看、编辑或删除他们不“拥有”的数据(无权查看、编辑或删除)。

因此您将需要将requestuser 对象传递给user_test 方法。例如:

from django.http import HttpRequest

class TestManager(models.Manager):
    def user_test(self, request_or_user):
        if isinstance(request_or_user, HttpRequest):
            return self.filter(user=request_or_user.user, viewed=False)
        else:
            return self.filter(user=request_or_user, viewed=False)

因此必须将request 对象从视图传递给函数。即使这也不是真正的纯粹。真正的方法只会接受user 对象:

class TestManager(models.Manager):
    def user_test(self, user):
            return self.filter(user=user, viewed=False)

所以在一个视图中可以这样使用:

def some_view(request):
    some_tests = Test.objects.user_test(request.user)
    # ...
    # return Http response

例如,如果我们想用这个查询集渲染一个模板,我们可以像这样传递它:

def some_view(request):
    some_tests = Test.objects.user_test(request.user)
    # ...
    return render(request, 'my_template.html', {'some_tests': some_tests})

【讨论】:

  • 在您发布答案之前,我试图在视图中创建一个函数,该函数将返回用户对象,然后将该函数导入我的模型中。但我无法在我的模型中导入函数:from .views import get_user 失败并出现ImportError
  • 您不能从模型的视图中导入函数是合乎逻辑的。通常视图应该依赖于模型,反之亦然。您可能构建了一个循环导入。
  • 好的 Sheldon ;) 我也尝试了您的解决方案,但是在模板中访问 user_test 时什么也没有返回。我的模板中的用法:{% if request.user.test.user_test %} <span> Yes </span>{% else %}<span> No </span>{% endif %} 总是给出No
  • 不幸的是,这是另一个反模式:(softwareengineering.stackexchange.com/a/250148/166310。你应该在视图中计算这个,所以像mytest = Test.objects.user_test(request.user)这样的东西,然后传递这个变量。
  • 不,这里mytest 是调用的结果。如您所见,这里调用objects.user_test(..)
猜你喜欢
  • 2012-01-28
  • 1970-01-01
  • 2012-07-02
  • 2016-11-08
  • 2011-11-15
  • 2020-11-04
  • 1970-01-01
  • 1970-01-01
  • 2012-11-16
相关资源
最近更新 更多