【问题标题】:Restrict User from view based on existing model根据现有模型限制用户查看
【发布时间】:2018-06-18 15:10:41
【问题描述】:

我的views.py 中有一些非常简单的视图。

class IndexView(generic.ListView):
    template_name = 'voting/index.html'
    context_object_name = 'latest_question_list'

    def get_queryset(self):
        """
        Return the last five published questions (not including those set to be
        published in the future).
        """    
        return Poll.objects.filter(
            pub_date__lte=timezone.now()
        ).order_by('-pub_date')[:5]


class DetailView(generic.DetailView):
    model = Poll
    template_name = 'voting/detail.html'
    context_object_name = 'question'

    def get_queryset(self):
        """
        Excludes any questions that aren't published yet.
        """
        return Poll.objects.filter(pub_date__lte=timezone.now())


class ResultsView(generic.DetailView):
    model = Poll
    template_name = 'voting/results.html'
    context_object_name = 'question'

我还创建了一个model,我在其中存储了哪些用户被邀请参加哪些投票以及其他一些数据。

class EligibleVoters(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
    poll = models.ForeignKey(Poll, on_delete=models.CASCADE, null=True)
    encrypted_keypart = models.BinaryField(max_length=200, blank=True)
    decrypted_keypart = models.BinaryField(max_length=200, blank=True)
    class Meta:
        unique_together = ["user", "poll"]

我想限制未受邀参加投票的用户查看这些投票。

我认为我想要为每个视图做这样的事情,但我不确定这是否是正确的方法。

class DetailView(generic.DetailView):
    model = Poll
    template_name = 'voting/detail.html'
    context_object_name = 'question'

    def get_queryset(self):
        """
        Excludes any questions that aren't published yet.
        """
        if EligibleVoters.objects.filter(poll=Poll.objects.id, user=self.request.user.id).exists():
            return Poll.objects.filter(pub_date__lte=timezone.now())
        else:
            return render('voting/somethingsomething.html')
            }) 

我应该以这种方式限制对特定民意调查的访问吗?此外,上面的代码并没有真正起作用并给出了一些错误,但我不确定是否应该继续并尝试以这种方式修复它。

【问题讨论】:

    标签: django django-models django-views django-permissions


    【解决方案1】:

    过滤eligblevoters上的查询集

    get_queryset渲染输出,它只产生一个查询集。

    尽管如此,我们可以通过添加额外的过滤来使用它来限制访问:

    class DetailView(generic.DetailView):
        model = Poll
        template_name = 'voting/detail.html'
        context_object_name = 'question'
    
        def get_queryset(self):
            return super(DetailView, self).get_queryset().filter
                eligiblevoters__user=self.request.user,
                pub_date__lte=timezone.now()
            )

    应该在其他视图中使用类似的方法。

    这是如何工作的

    查询的工作方式如下。通过定义从EligibleVotersUser(命名为user)的ForeignKey,然后Django 会创建一个反向的隐式 关系:例如,您可以使用User.eligblevoters_set 查询以获得相关的EligableVoters 查询集。我们也可以使用elgiblevoters对其进行过滤。

    因此,我们添加了两个额外的过滤条件:pub_date__lte=timezone.now() 过滤之前发布的帖子(现在包括在内)和eligiblevoters__user=self.request.user。这意味着我们添加了一个约束,至少其中一个相关的eligablevoters 应该具有userself.request.user(此特定会话的用户)。

    所以如果没有这样的Post,那么我们就不能得到Poll和所请求的id

    在找不到对象的情况下渲染

    我们还可以在找不到对象的情况下渲染页面,例如通过使用try-exceptHttp404 修补.get(..) 函数,然后渲染特定页面,例如:

    from django.http import Http404
    
    class DetailView(generic.DetailView):
        model = Poll
        template_name = 'voting/detail.html'
        context_object_name = 'question'
    
        def get_queryset(self):
            return super(DetailView, self).get_queryset().filter
                eligiblevoters__user=self.request.user,
                pub_date__lte=timezone.now()
            )
    
        def get(request, *args, **kwargs):
            try:
                return super(DetailView, self).get(request, *args, **kwargs)
            except Http404:
                return render(request, 'app/template_not_found.html', {})

    所以我们在这里渲染一个(可能不同的模板),这里有一个空的上下文,尽管你当然可以让它更高级。

    【讨论】:

    • 您提到get_queryset 不会呈现输出。这是否意味着这不是正确且安全的方法?您的解决方案有效,但如何调整它并在未邀请用户时给出错误消息,类似于“您未受邀参加任何投票”或“您无权访问此投票”?
    • 您能否进一步解释一下您的解决方案以及它为何有效?尤其是这一行eligiblevoters__user=self.request.user。我是 django 新手,我正在努力学习。
    • @kstr:给我几分钟。
    • 您的答案适用于除 IndexView 之外的所有视图。我收到此错误:IndexView is missing a QuerySet. Define IndexView.model, IndexView.queryset, or override IndexView.get_queryset().
    • @kstr:您是否在您的IndexView 中添加了model=Poll
    【解决方案2】:

    您应该添加一个从 Poll 到 User 的多对多字段,使用 EligibleVoter 作为通过字段:

    eligible_users = models.ManyToManyField('User', through='EligibleVoter')
    

    现在你可以这样做了:

    return Poll.objects.filter(eligible_users=self.request.user, pub_date__lte=timezone.now())
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-22
      • 2022-10-24
      • 1970-01-01
      • 2023-03-31
      • 1970-01-01
      • 2021-02-13
      • 2015-04-18
      • 2014-09-28
      相关资源
      最近更新 更多