Как структурировать модели для прогнозирования приложения? - PullRequest
0 голосов
/ 26 октября 2019

В Django я ищу рекомендации о том, как структурировать модели для приложения прогнозирования, которое похоже на приложение для опросов или опросов, но не совсем.

Обзор требований:

(1) В тесте будет несколько вопросов.

(2) Вопросы могут принимать несколько форм - True или False, множественный выбор с 3 вариантами, множественный выбор с 4 вариантами и т. Д.

(3) Пользователи представляют прогнозы (или ответы) для каждого вопроса в форме вероятностей с ограничением, что общая вероятность составляет 100%. Таким образом, для вопроса № 1 с тремя вариантами AC пользователь может прогнозировать A: 30%, B: 50%, C: 20%

(4) Каждый вопрос имеет 1 правильный ответ.

[Вопросы оцениваются с использованием оценки Бриера, но это не существенно для этого обсуждения.]

Я знаком с приложением для опроса в учебнике Django и смотрел несколько приложений для викторин, но ни одно из них не решает мою проблему.

Если я использую структуру учебника опроса Django с неопределенным числом вариантов, то я не могу понять, как организовать прогноз пользователя на вопрос - так как этот прогноз должен иметь вероятность для каждого варианта ивероятности должны составлять до 100%.

Если я создаю несколько моделей вопросов, таких как TrueFalseQuestion, MultipleChoice3OptionsQuestion и т. д., тогда мои представления и шаблоны становятся громоздкими, поскольку я не могу просто установить контекст для модели Вопроса.

Я подумал о создании родительского класса Question с abstract = True, а затем о создании дочерних классов типа "classTFQuestion (вопрос):». Но, опять же, это ограничивает мою способность использовать общие шаблоны, такие как ListView, так как у меня теперь есть много дочерних классов.

Подводя итог, можно сказать, что моя проблема отличается от всего, что я могу найти, это: Большинство приложений викторины имеютПользователь предоставляет один выбор из нескольких вариантов, независимо от того, сколько вариантов. Мое приложение требует ответа (прогноз), который состоит из вероятности для каждого варианта выбора, а также ограничение, что вероятности равны 100%.

************* ДОБАВЛЕНИЕ БОЛЬШЕ ДЕТАЛИНИЖЕ *************

Для упрощения, давайте предположим, что я хочу, чтобы у каждого вопроса было 3 варианта множественного выбора. В этом случае каждый пользовательский прогноз будет состоять из набора из 3 вероятностей и комментария.

Первый вопрос: существует ли лучший способ структурировать приведенные ниже модели для случая с 3 вариантами ответа?

class Quiz(models.Model):
   name = models.CharField(max_length=20)
   owner = models.ForeignKey(User, on_delete=models.CASCADE)

class Question(model.Model):
   quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE)
   question_text = models.CharField(max_length=200)
   choice1 = models.CharField(max_length=200)
   choice2 = models.CharField(max_length=200)
   choice3 = models.CharField(max_length=200)
   correct_choice = models.IntegerField()

class Forecast(model.Model):
   question = models.ForeignKey(Question, on_delete=models.CASCADE)
   user = models.ForeignKey(User, on_delete=models.CASCADE)
   comment = models.CharField(max_length=200)
   prob1 = models.IntegerField()
   prob2 = models.IntegerField()
   prob3 = models.IntegerField()

Если это хорошая структура для вопросов с 3 вариантами, то как бы я расширил это в соответствии с моими первоначальными требованиями иметь вопросы с 2, 3, 4, 5 вариантами?

Моя проблема заключается в том, чтоесли я использую структуру в приложении опроса учебника Django, где число вариантов выбора не определено, то моя модель прогнозирования выше ломается, так как в ней жестко заданы 3 варианта.

1 Ответ

0 голосов
/ 26 октября 2019

Похоже, что подобная структура будет работать для вас (с истинно-ложными вопросами, смоделированными как вопросы с двумя вариантами ответов):

class Quiz(models.Model):
   name = models.CharField(max_length=20)
   owner = models.ForeignKey(User, on_delete=models.CASCADE)


class Question(model.Model):
   quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE)
   text = models.CharField(max_length=200)
   correct_choice = models.ForeignKey('Choice')


class Choice(model.Model):
   question = models.ForeignKey(Question, on_delete=models.CASCADE)
   text = models.CharField(max_length=200)


class Forecast(model.Model):
   question = models.ForeignKey(Question, on_delete=models.CASCADE)
   user = models.ForeignKey(User, on_delete=models.CASCADE)
   comment = models.CharField(max_length=200)

   class Meta:
       unique_together = (('question', 'user'),)


class ForecastChoice(model.Model):
   forecast = models.ForeignKey(Forecast, on_delete=models.CASCADE)
   choice = models.ForeignKey(Choice, on_delete=models.CASCADE)
   probability = models.IntegerField()

   class Meta:
       unique_together = (('forecast', 'choice'),)

Ограничения, не моделируемые с помощью unique_together, оставлены в качестве упражнениядля читателя:

  • прогноз должен содержать столько ForecastChoices, сколько есть вариантов в вопросе
  • вероятность ForecastChoices прогноза должна составлять до 100

Возможно, вам также понадобится поле ordering для Вопросов, если это имеет значение здесь, а также обычные метаданные, такие как время создания для Forecast с.

РЕДАКТИРОВАТЬ Как просили в комментариях, пример ForecastForm, который динамически создавал поля вероятности для каждого выбора и представление для его управления. Это сухой код, поэтому возможны глупые ошибки, но идея твердая.

class ForecastForm(forms.ModelForm):
    class Meta:
        model = Forecast
        fields = ('comment',)  # tell Django to only create this field

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        # Build dynamic fields.
        self.probability_fields = []
        for choice in self.instance.question.choice_set.all():
            name = f'probability_{choice.id}'
            field = forms.IntegerField(
                label=f'{choice.text} – probability',
                min_value=0,
                max_value=100,
                required=True,
            )
            # Put the field in the form...
            self.fields[name] = field
            # ... and store the name + choice object for later validation use
            self.probability_fields.append((name, choice))

    def clean(self):
        super().clean()
        probability_sum = 0
        for name, choice in self.probability_fields:
            probability = self.cleaned_data[name]
            probability_sum += probability
        if probability_sum != 100:
            raise forms.ValidationError(f'Probabilities sum up to {probability_sum}, not the expected 100.')

    def save(self):
        with transaction.atomic():
            super().save()  # will save the `self.instance` Forecast object with the comment
            for name, choice in self.probability_fields:
                ForecastChoice.objects.create(
                    forecast=self.instance,
                    choice=choice,
                    probability=self.cleaned_data[name],
                )

# This is a slightly unorthodox updateview in that the form it's driving
# is not directly related to the model the underlying "detail view" is acquiring.

class QuestionForecastView(views.UpdateView):
    model = Question

    def get_form(self, form_class=None):
        # Unsaved forecast initialized with the question and user context.
        forecast = Forecast(
            question=self.get_object(),
            user=self.request.user,
        )
        kwargs = self.get_form_kwargs()
        kwargs['instance'] = instance
        return ForecastForm(**kwargs)
...