使用django表单保存新外键

使用django表单保存新外键,django,django-forms,Django,Django Forms,我有两种型号: 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, uni

我有两种型号:

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)
我有一个电影表单,允许用户选择一个先前存在的工作室,或键入一个新的工作室:

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")
需要进行验证以确保新的_studio名称不存在。如果用户进入一个新的工作室,我想保存工作室,然后保存新的电影

form = FilmForm(request.POST) 
if form.is_valid(): # All validation rules pass
    std = Studio(name = form.cleaned_data['new_studio'])
    std.save()
但是,如何根据全新的工作室id保存电影实例?我看过,但是如果我在电影模型和电影形式中有更多的领域呢?如果我使用链接答案,我必须输入每个字段:

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.)

实现这一点的正确方法是什么?

实际上,您唯一的问题是使用了标准的
表单
,而不是
模型表单
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()

我偶然发现了这个问题,我想我为未来的求职者找到了一个更好的答案

Django附带了一个名为的工具,它可以帮助您在表单中管理关系

您只需像使用各自的外键一样创建模型。 然后在Forms.py中导入

from django.forms.models import inlineformset_factory
然后:

回到yout views.py,您应该导入表单和新创建的表单集

完成此操作后,您的方法应使用如下所示的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,})
我用的是我自己的例子,但这个想法已经解释清楚了


如果你想读一个更具体的解释,请阅读我发现的这篇很棒的文章

哦,嗯,很好!我想我得走很长一段路来补偿增加一个新工作室的可能性。谢谢不过我有一个问题。我为一个重复的工作室创建了一个验证检查(如果用户进入一个已经存在的新工作室)。代码正确地到达了ValidationError,但似乎仍在继续清理,并给出了错误:无法分配None:“Film.studio”不允许空值。
clean
方法解决了所有这些问题。如果
new\u studio
存在,它将只获取现有的studio,而不是实际创建一个新的studio(
get\u或\u create
),因此不需要进行验证检查。上述
clean
方法未涵盖的唯一情况是,如果输入了
studio
,但没有输入
new\u studio
。然而,这不会是一个错误,在这种情况下不需要做任何特殊的事情。我想不管你在做什么验证检查,都会在某种程度上弄乱这些值。明白了。但是当我把studio和new_studio都留为空白并且删除了重复的检查之后,我仍然会得到错误。如果您愿意评论,请在此修改问题?希望它能帮助更多的人:)链接提供404
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,})