Валидация моделей в админке django с помощью инлайнов (когда я сохраняю несколько моделей одновременно)

Я создаю викторину на django. На картинке ниже вы можете видеть 3 модели (Quiz, QuizQuestionQuizAnswer) на 1 странице. Для этого я использовал NestedStackedInline и NestedTabularInline.

Мне нужно, чтобы при сохранении редактируемых форм происходила валидация теста. Например:

  • Нужно проверить, отмечен ли хотя бы один ответ как правильный.
  • Если ответов больше одного. и т.д.

enter image description here

models.py

class Quiz(models.Model):
    title = models.CharField('Название теста', max_length=200)
    description = models.TextField('Описание', max_length=2000, blank=True)
    owner = models.ForeignKey(User, on_delete=models.DO_NOTHING)
    pass_score = models.PositiveIntegerField(help_text='Минимальный результат (в процентах) для прохождения теста',
                                             default=60,
                                             validators=[
                                                 MaxValueValidator(100),
                                             ])
    created_at = models.DateTimeField('Дата создания', auto_now_add=True, )
    updated_at = models.DateTimeField('Дата обновления', auto_now=True, )

    def get_quiz_question(self):
        return self.quizquestion_set.all()

    def __str__(self):
        return self.title

class QuizQuestion(models.Model):
    """ Вопросы для квиза"""
    quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE)
    question = models.CharField('Вопрос', max_length=1000)
    created_at = models.DateTimeField('Дата создания', auto_now_add=True, )
    updated_at = models.DateTimeField('Дата обновления', auto_now=True, )

    def __str__(self):
        return self.question

    class Meta:
        verbose_name = 'Тест'
        verbose_name_plural = 'Тесты'

class QuizAnswer(models.Model):
    """ Варианты ответов """
    question = models.ForeignKey(QuizQuestion, on_delete=models.CASCADE)
    text = models.CharField('Варианты ответа', max_length=250)
    is_correct = models.BooleanField('Отметьте правильные', default=False)

    class Meta:
        verbose_name = 'Ответ'
        verbose_name_plural = 'Ответы'

    def __str__(self):
        return f'Вариант ответа'

admins.py

from quiz.models import *
from django.contrib import admin
from nested_inline.admin import NestedStackedInline, NestedModelAdmin, NestedTabularInline


class AnswerInLine(NestedTabularInline):
    """ Делаем вложенную модель в админке"""
    model = QuizAnswer
    extra = 1


class QuestionInLine(NestedStackedInline):
    """ Делаем вложенную модель в админке"""
    model = QuizQuestion
    extra = 2
    inlines = [AnswerInLine]

    def save_model(self, request, obj, form, change):
        pass


class QuizAdmin(NestedModelAdmin):
    # добавляет эти поля в админку приложения
    # list_display = ('title','description','pass_score')
    model = Quiz
    inlines = [QuestionInLine]
    readonly_fields = ('owner', 'created_at', 'updated_at')

    def save_model(self, request, obj, form, change):

        if not obj.pk:
            # Only set added_by during the first save.
            obj.owner = request.user
        super().save_model(request, obj, form, change)




class CategoryAdmin(admin.ModelAdmin):
    list_display = ("id", 'title')
    list_display_links = ('id', 'title')
    search_fields = ('title',)


# регистрирует модуль и его параметры
admin.site.register(Quiz, QuizAdmin)

Лучший способ сделать это - определить пользовательский набор форм, как показано ниже:

form.py

from django import forms
from quiz.models import *


class AnswerModelForm(forms.models.BaseInlineFormSet):
    class Meta:
        model = QuizAnswer
        fields = "__all__"

    def clean(self):
        true_answer_count = 0
        answer_count = 0
        for form in self.forms:
            try:
                if form.cleaned_data.get("is_correct") is True:
                    true_answer_count += 1
                answer_count += 1
            except AttributeError:
                pass
        if true_answer_count > 1:
            raise forms.ValidationError(
                ("A question can not have more than one correct answer")
            )
        if answer_count not in [2, 3, 4]:
            raise forms.ValidationError(
                ("A question can have only 2, 3, 4 answers")
            )
        if true_answer_count == 0:
            raise forms.ValidationError(
                ("A question must have at least one correct answer")
            )

Вы можете легко подсчитать все правильные ответы и общее количество ответов на ваш вопрос и реализовать с их помощью свои собственные проверки

.

И вам обязательно нужно добавить свой собственный набор форм в AnswerInLine, как показано ниже:

admin.py

from quiz.models import *
from django.contrib import admin
from quiz.forms import *
from nested_inline.admin import NestedStackedInline, 
NestedModelAdmin, NestedTabularInline


class AnswerInLine(NestedTabularInline):
    formset=AnswerModelForm
    """ Делаем вложенную модель в админке"""
    model = QuizAnswer
    extra = 1


class QuestionInLine(NestedStackedInline):
    model = QuizQuestion
    extra = 2
    inlines = [AnswerInLine]
#rest of your code...
Вернуться на верх