【问题标题】:Where to check for 403 in a Django CBV?在哪里检查 Django CBV 中的 403?
【发布时间】:2015-07-15 23:35:49
【问题描述】:

我正在制作一个教初学者的基本应用程序。每个用户都可以写笔记,但我想让用户无法查看或更新其他用户的笔记。

我有以下观点,但我不得不重复自己。

from django.core.exceptions import PermissionDenied

...

class NoteUpdate(LoginRequiredMixin, UpdateView):

    ...

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()

        if self.object.owner != self.request.user:
            raise PermissionDenied

        return super(NoteUpdate, self).get(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()

        if self.object.owner != self.request.user:
            raise PermissionDenied

        return super(NoteUpdate, self).post(request, *args, **kwargs)

我觉得可能有一种方法可以做到这一点而无需重复自己。是的,我可以编写这样的方法并从两者中调用它:

def check_permission(self):
   if self.object.owner != self.request.user:
        raise PermissionDenied 

但我真正的意思是我是否覆盖了错误的方法?有没有更传统的方法来做到这一点?覆盖 .get() 和 .post() 感觉有点奇怪

【问题讨论】:

    标签: django django-views django-class-based-views


    【解决方案1】:

    回答您的问题:覆盖 .get().post() 很好,因为出于安全和完整性原因,您希望在显示和特别是修改数据之前验证您的 get()post() 视图。现在,如果您想在 get 或 post 中进行重构,有两种简单的方法:

    初级(模型方法):

    models.py

    class Model(models.Model):
        owner = models.ForeignKey(User)
        ...
    
        def deny_if_not_owner(self, user):
            if self.owner != user:
                raise PermissionDenied
            return self.owner
    

    views.py

    class NoteUpdate(LoginRequiredMixin, UpdateView):
        ...
    
        def get(self, request, *args, **kwargs):
            self.object = self.get_object()
    
            self.object.deny_if_not_owner(request.user)
    
            return super(NoteUpdate, self).get(request, *args, **kwargs)
    
        def post(self, request, *args, **kwargs):
            self.object = self.get_object()
    
            self.object.deny_if_not_owner(request.user)
    
            return super(NoteUpdate, self).post(request, *args, **kwargs)
    

    ///////

    替代(混合):

    如果您以后看到自己再次使用此验证,创建一个 Mixin 将允许您轻松将此代码添加到许多类中。

    class DenyWrongUserMixin(object):
        def get(self):
            if self.object.owner != self.request.user:
                raise PermissionDenied
            return super(DenyWrongUserMixin, self).get(*args, **kwargs)
    
        def post(self):
            if self.object.owner != self.request.user:
                raise PermissionDenied
            return super(DenyWrongUserMixin, self).post(*args, **kwargs)
    

    然后:

    class NoteUpdate(LoginRequiredMixin, DenyWrongUserMixin, UpdateView):
    
        ...
    
        def get(self, request, *args, **kwargs):
            ...
    
        def post(self, request, *args, **kwargs):
            ...
    

    【讨论】:

    • 我以为您正在尝试重构以实现可扩展使用。我更新了我的答案以直接回答您的问题“是否覆盖 getpost 正确 - 我的答案是肯定的。您可以查看我修改后的答案以了解原因,主要解决方案显示如何将一些验证带到模型级别以减少视图中的代码,同时允许以后轻松实现。
    • 感谢您更新的答案。这是一个关于制作厚模型和薄视图的一个很好的提醒,尽管它确实让我在模型中放置 HTTP 响应内容的错误方式。 (我删除了我之前的评论,因为它不适用于您关于 mixins 的新评论,尽管现在我看到您回复了它。对不起!)。
    【解决方案2】:

    您可以覆盖 get 方法或 get_queryset 方法。如果登录用户不是所有者,get_queryset 将引发 404。

    def get_queryset(self):
        qs = super(NoteUpdate, self).get_queryset()
        return qs.filter(owner=self.request.user)
    

    或者你可以重写 get 方法,因为它会先被调用,然后引发 PermissionDenied,所以没有理由也重写 post 方法。

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        if self.object.owner != self.request.user:
            raise PermissionDenied
        return super(NoteUpdate, self).get(request, *args, **kwargs)
    

    然后你可以创建一个 mixin 并从 mixin 扩展你的视图以避免重复。

    【讨论】:

    • 如果我覆盖 GET 但不覆盖 POST 我明白我会防止意外发生(因为他们无法查看表单页面以通过网络提交)但仍然不能有人有恶意吗?
    猜你喜欢
    • 2016-01-10
    • 1970-01-01
    • 2018-05-06
    • 2011-08-22
    • 2013-07-04
    • 1970-01-01
    • 2012-09-05
    • 2011-07-27
    • 1970-01-01
    相关资源
    最近更新 更多