Где я должен установить поля модели, производные от поля формы?
Если модель имеет некоторые поля, которые не отображаются непосредственно на поля 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, так как он не имеет этих недостатков.