Как справиться с проверкой формы модели, когда модель имеет чистый метод, если форма модели исключает некоторые поля? - PullRequest
1 голос
/ 06 октября 2019

У меня есть эта модель:

class IeltsExam(Model):

    student = OneToOneField(Student, on_delete=CASCADE)
    has_taken_exam = BooleanField(default=False,)
    listening = FloatField(choices=SCORE_CHOICES, null=True, blank=True, )
    reading = FloatField(choices=SCORE_CHOICES, null=True, blank=True, )
    exam_date = DateField(null=True, blank=True, )

    non_empty_fields = \
        {
            'listening': 'please enter your listening score',
            'reading': 'please enter your reading score',
            'exam_date': 'please specify your exam date',
        }

    def clean(self):
        errors = {}
        if self.has_taken_exam:
            for field_name, field_error in self.non_empty_fields.items():
                if getattr(self, field_name) is None:
                    errors[field_name] = field_error
        if errors:
            raise ValidationError(errors)

, и у меня есть эта модель

class IeltsExamForm(ModelForm):

    class Meta:
        model = IeltsExam
        fields = ('has_taken_exam', 'listening', 'reading', )

, когда я отправляю эту форму в шаблоне, я получаю следующую ошибку:

ValueError at /
'ExamForm' has no field named 'exam_date'.

и

During handling of the above exception ({'listening': ['please enter your listening score'], 'reading': ['please enter your reading score'], 'exam_date': ['please specify your exam date']}), another exception occurred:

Ошибка происходит на мой взгляд, когда я проверяю форму. Моя логика базы данных такова, что мне нужно иметь поле exam_date, и оно должно быть обязательным для заполнения, если проверен has_taken_exam. Однако в ExamForm по деловым причинам мне не нужна дата экзамена. Как я могу сказать ExamForm закрыть глаза на дату экзамена, так как я не сохраняю экземпляр модели?

Ответы [ 2 ]

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

После инициализации ModelForm он имеет атрибут instance, который является экземпляром модели, для которого будет вызываться clean(). Поэтому, если вы удалите exam_date из словаря non_empty_fields экземпляра, он не будет использовать его в clean:

class IeltsExamForm(ModelForm): 
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.instance.non_empty_fields.pop('exam_date')

И вы можете сделать это для каждого поля в self._meta.exclude.

Однако при этом атрибут non_empty_fields должен быть не атрибутом класса, а свойством экземпляра. Изменение экземпляра non_empty_fields фактически изменяет атрибут класса (это словарь, поэтому он изменчив), что будет иметь непредвиденные побочные эффекты (после удаления он удаляется для любого последующего создаваемого вами экземпляра). Измените вашу модель, чтобы установить атрибут в методе init:

class IeltsExam(Model):
    # ...
    # remove the class attribute non_empty_fields

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.non_empty_fields = { ... }

В общем, я бы посоветовал вам использовать ModelForm, только если вы действительно собираетесь сохранить модель, в этом случаеАтрибут класса - более чистый подход. Вместо того, чтобы делать все это, если ваша форма не будет сохранять фактическую модель, вы не должны использовать ModelForm, а Form и определять все поля и очистку в самой форме.

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

Выполните проверку для модели save()

Рассмотрим следующую модель:

class Exam(Model):

    student = OneToOneField(Student, on_delete=CASCADE)
    has_taken_exam = BooleanField(default=False)
    score = FloatField(choices=SCORE_CHOICES, null=True, blank=True)
    exam_date = DateField(null=True, blank=True)

    def save(self, *a, **kw):
        if self.has_taken_exam and not self.exam_date:
            raise ValidationError("Exam date must be set when has_taken_exam is True")
        return super().save()
...