首页 > 解决方案 > Django 验证特定问题的答案

问题描述

概述:我想建立一个问答网站,用户必须在其中输入每个问题的正确答案。为此,我制作了 3 个模型:

class ProblemSet(models.Model):
    id = models.IntegerField(primary_key=True)

class Problem(models.Model):
    id = models.IntegerField(primary_key=True)
    problem_set = models.ForeignKey(ProblemSet, on_delete=models.CASCADE)
    question = models.TextField()
    solution = models.TextField()

class Solve(models.Model):
    username = models.ForeignKey(User, on_delete=models.CASCADE)
    problem_set = models.ForeignKey(ProblemSet, on_delete=models.CASCADE)
    problem_id = models.ForeignKey(Problem, on_delete= models.CASCADE)

在求解模型中,如果有任何条目表示特定用户已解决该问题 ID。所以,我使用了通用的表单视图:

class IndexView(FormView):
    form_class = ProblemForm
    template_name = 'home/index.html'
    success_url = reverse_lazy('index')

    def get_context_data(self, **kwargs):
        context = super(IndexView, self).get_context_data(**kwargs)
        if self.request.user.is_authenticated:
            inner_qs = "fetch ids that are solved from Solve model"

            problem_obj = Problem.objects\
                            .exclude(id__in=inner_qs)\
                            .order_by('id').first()

        else:
            #do something

        context['question'] = problem_obj.question
        return context

问题形式为:

from django import forms

class ProblemForm(forms.Form):
    solution = forms.CharField(widget=forms.TextInput())

如何验证用户输入的答案是否正确?我确实在 def form_valid(self, form) 函数中获得了解决方案字段的值,但我应该如何处理它?我应该在 context 中传递 question_id 并在 form_valid 中查询数据库,还是应该将解决方案本身传递给 context 并在 form_valid() 方法中访问上下文数据以防止双重查询,但在这种方法中,我不确定这是否安全,因为我不不希望将解决方案传递给客户。

有什么优雅的方法可以做到这一点吗?

PS 在将用户输入的解决方案与该问题的数据库中的解决方案进行比较后,我在 Solve 表中添加一个条目,表示该特定用户已解决问题 ID。

标签: djangodjango-forms

解决方案


正在处理两个单独的FormView请求:首先是学生获取带有问题要回答的表单时的 GET 请求。然后是学生提交问题答案时的 POST 请求。

现在 HTTP 是无状态的,因此您需要以某种方式跟踪第一个请求中提出的问题,以便您知道在接收 POST 请求时回答了哪个问题。

我想说的最简单的方法是实际上将 包含question_id在表单本身中,作为隐藏的输入字段。这里没有真正的安全问题:question_id即使隐藏也可以被学生操纵,但有什么意义呢?

所以这就是我要做的:

  • 添加problem一个ModelChoiceField带有HiddenInput小部件的ProblemForm.
    problem = forms.ModelChoiceField(queryset=Problem.objects.all(), widget=forms.HiddenInput())
    
  • problem在你的get_inital()方法中设置一个初始值IndexView

    def get_problem(self):  # use also in get_context_data() to add the question
        if hasattr(self, 'problem'):
            return self.problem
        if self.request.user.is_authenticated:
            inner_qs = "fetch ids that are solved from Solve model"
            self.problem = Problem.objects\
                            .exclude(id__in=inner_qs)\
                            .order_by('id').first()
            return self.problem
    
    def get_initial(self):
        initial = super().get_initial()
        initial['problem'] = self.get_problem()}
        return initial
    
  • 当表单被提交并且有效时,您会看到这form.cleaned_data['problem']是提交的问题。所以你可以在form_valid()方法中使用它:
    def form_valid(self, form):
        problem = form.cleaned_data['problem']
        # check that it hasn't been solved by the user already
        if problem.answer == form.cleaned_data['solution']:
            # create solve object for the user
        return redirect(...)
    

另一种方法是不将其包含在表单中,而是重新获取problemin form_valid(请注意,当表单将提交的内容映射到实际实例以填充其problem时,上述方法中也会获取)。problem_idproblemcleaned_data


推荐阅读