如何在Django模型中安全地访问请求对象

时间:2018-09-07 09:48:11

标签: django django-models django-request

我要做什么:

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

我尝试过的事情:

我在this网站上发现了黑客。但是评论中的某人指出,在生产中不要这样做。

我也尝试覆盖模型的__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()

1 个答案:

答案 0 :(得分: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对象从视图传递到函数。即使这不是真正的。真正的 pure 方法只能接受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})