我有两种模式:
class Studio(models.Model):
name = models.CharField("Studio", max_length=30, unique=True)
class Film(models.Model):
studio = models.ForeignKey(Studio, verbose_name="Studio")
name = models.CharField("Film Name", max_length=30, unique=True)
我有一个电影表单,允许用户选择一个预先存在的Studio,或键入一个新表单:
class FilmForm(forms.Form):
studio = forms.ModelChoiceField(Studio.objects, required=False)
new_studio = forms.CharField(max_length=30, required=False, label = "New Studio Name")
name = forms.CharField(max_length=30, label = "Film Name")
确认new_studio名称尚不存在。如果用户输入new_studio,我想保存工作室,然后保存新的电影。
form = FilmForm(request.POST)
if form.is_valid(): # All validation rules pass
std = Studio(name = form.cleaned_data['new_studio'])
std.save()
但是,如何根据全新的工作室ID保存电影实例?我见过this question,但是如果我在电影模型和电影形式中有更多的字段呢?如果我使用链接的答案,我将不得不输入每个字段:
studio = Studio.objects.get(name=request.POST['new_studio'])
newFilm=Film(name=form.name, studio=studio, field_one = form.field_one, field_two = form.field_two, etc.)
实现此目的的正确方法是什么?
答案 0 :(得分:5)
真的,您唯一的问题是您使用的是标准Form
而不是ModelForm
。 Form
没有save
方法,因为它本身并不与任何东西联系在一起(即它不知道要保存什么或保存到哪里)。
但是,如果您使用ModelForm
,则需要处理在表单中创建新工作室所涉及的所有逻辑。这实际上更好,因为那样你就可以使用表单而不用担心其他任何事情:表单包含正确保存自己所需的所有逻辑。
class FilmForm(forms.ModelForm):
class Meta:
model = Film
# only need to define `new_studio`, other fields come automatically from model
new_studio = forms.CharField(max_length=30, required=False, label = "New Studio Name")
def __init__(self, *args, **kwargs):
super(FilmForm, self).__init__(*args, **kwargs)
# make `studio` not required, we'll check for one of `studio` or `new_studio` in the `clean` method
self.fields['studio'].required = False
def clean(self):
studio = self.cleaned_data.get('studio')
new_studio = self.cleaned_data.get('new_studio')
if not studio and not new_studio:
# neither was specified so raise an error to user
raise forms.ValidationError('Must specify either Studio or New Studio!')
elif not studio:
# get/create `Studio` from `new_studio` and use it for `studio` field
studio, created = Studio.objects.get_or_create(name=new_studio)
self.cleaned_data['studio'] = studio
return super(FilmForm, self).clean()
然后,在您看来,您所需要的只是:
if form.is_valid():
form.save()
答案 1 :(得分:2)
我偶然发现了这个问题,我想我为未来的求职者找到了更好的答案。
Django附带了一个名为Inline Formsets的东西,它可以帮助您管理表单中的关系。
您只需像创建各自的ForeignKeys一样创建模型。 然后在Forms.py中导入此
from django.forms.models import inlineformset_factory
然后:
YOUR_FORMSET = inlineformset_factory(Model1,Model2)
回到yout views.py,您应该导入表单和新创建的YOUR_FORMSET。
执行此操作后,您的方法应使用如下的inlineformset:
def add_question(request):
if request.method == 'POST':
form =AddQuestionForm(request.POST, request.FILES)
if form.is_valid():
new_question = form.save(commit=False)
question_formset = QuestionFormset(request.POST, instance=new_question)
if question_formset.is_valid():
form.save()
question_formset.save()
return HttpResponseRedirect(reverse('polls:detail',args=(new_question.pk,)))
else:
print(form.errors)
else:
form = AddQuestionForm()
question_formset = QuestionFormset(instance=Question())
return render(request, 'polls/add.html', {'form':form, 'question_formset':question_formset,})
我正在使用我自己的例子,但解释了这个想法。
如果您想阅读更具体的解释,请阅读我发现的这个令人敬畏的blog post。