Django подтвердить ответ на конкретный c вопрос - PullRequest
0 голосов
/ 18 февраля 2020

Обзор: я хочу создать веб-сайт «Вопрос-ответ», на котором пользователь должен вводить правильный ответ на каждый вопрос. Для этого я сделал 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)

В модели решения, если есть какая-либо запись, означающая, что конкретный пользователь решил эту проблему. Итак, я использовал обобщенный c Просмотр формы:

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())

Как мне проверить, что пользователь вводит правильный ответ? Я получаю значение поля solution в функции def form_valid (self, form), но как мне с этим бороться? Должен ли я передать question_id в контексте и запросить базу данных в form_valid, или я должен передать само решение для контекста и получить доступ к данным контекста в методе form_valid (), чтобы предотвратить двойной запрос, но в этом методе я не уверен, насколько это безопасно, поскольку я не Я не хочу, чтобы решение передавалось клиенту.

Есть ли какой-нибудь элегантный способ сделать это?

PS После того, как введенное пользователем решение сравнивается с данным в базе данных для этого вопроса, я добавляю запись в таблицу Solve, обозначающую, что этот конкретный пользователь решил вопрос id.

1 Ответ

1 голос
/ 19 февраля 2020

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(...)
    

В качестве альтернативы можно было бы не включать его в форму, а повторно ввести problem в form_valid (обратите внимание, что problem извлекается также в вышеуказанном методе, когда форма отображает отправленный problem_id фактическому problem экземпляру, чтобы заполнить его cleaned_data).

...