Где я должен установить поля модели, производные от поля формы?

Если модель имеет некоторые поля, которые не отображаются непосредственно на поля ModelForm, где я должен установить их значение? Далее, form.blast_db должен быть присвоен либо model.protein_db, либо model.nucleotide_db в зависимости от того, какой это тип взрывной БД. Они являются взаимоисключающими, но необходимыми, поэтому должны быть установлены до вызова model.full_clean().

from django.db import models

class BlastQuery(models.Model):

    # Protein/nucleotide blast DB. Mutually exclusive, one must be provided
    protein_db = models.ForeignKey(ProteinDB, null=True, blank=True)
    nucleotide_db = models.ForeignKey(ProteinDB, null=True, blank=True)

    # Just 1 field to keep it terse but in reality there are many other fields
    foo = models.IntegerField()

    def clean(self):
        if self.protein_db and self.nucleotide_db:
            raise ValidationError('Protein/nucleotide DB are mutually exclusive')
        if not self.protein_db and not self.nucleotide_db:
            raise ValidationError('Protein/nucleotide DB are required')
from django import forms

class BlastForm(forms.ModelForm):

    # Magical field that's like a ModelChoiceField but can
    # hold both ProteinDB and NucleotideDB instances
    blast_db = BlastDBField()

    class Meta:
        model = BlastQuery
        fields = ('foo',)

Вы можете установить производные поля в Form.clean:

class BlastForm(forms.ModelForm):

    # ...

    def clean(self):
        super().clean()

        # Validate/modify self.cleaned_data
        # ...
        
        # Derive the protein/nucleotide db field
        blast_db = self.cleaned_data.get('blast_db')
        if blast_db:
            if blast_db.type == 'protein':
                self.instance.protein_db = blast_db
            else:
                self.instance.nucleotide_db = blast_db

ModelForm обновляет self.instance с self.cleaned_data после form.clean()_post_clean) и вызывает model.full_clean(), поэтому если бы вы изменили self.instance в form.save(), вы бы опоздали, чтобы предотвратить ошибку валидации. Также обратите внимание, что только Meta.fields, которые не появляются в Meta.exclude, применяются от self.cleaned_data к self.instance, поэтому установка protein_db/nucleotide_db в self.cleaned_data тоже ничего не даст. Вы можете переопределить Model.clean_fields(exclude) вместо Model.clean и пропустить проверку protein_db и nucleotide_db, когда они находятся в exclude, но тогда они никогда не будут проверены, если вы не вызовете form.full_clean() снова перед сохранением. Поэтому просто поместите его в Form.clean, так как он не имеет этих недостатков.

Вернуться на верх