I'm trying to prepopulate a ModelForm and an inlineformset_factory with an instance of a Model, BUT when the user submits the form, I need to create a new instance of the Model and it's related child records.
我正在尝试使用Model的实例预填充ModelForm和inlineformset_factory,但是当用户提交表单时,我需要创建Model的新实例及其相关的子记录。
Example Model:
class Artist(models.Model):
artist = models.CharField(max_length=100)
class Song(models.Model):
artist = models.ForeignKey(Artist)
song = models.CharField(max_length=200)
I want the user to see an edit form based on an instance of Artist, along with an InlineFormSet for that Artist's related Songs. The form will be prepopulated with the existing data and the user can change the artist's name and the song names. However, when the user submits the form, I don't want to overwrite the existing records. Instead, I want to create a new instance of Artist and add new Songs for this new artist.
我希望用户看到基于Artist实例的编辑表单,以及该Artist相关歌曲的InlineFormSet。表格将预先填充现有数据,用户可以更改艺术家的姓名和歌曲名称。但是,当用户提交表单时,我不想覆盖现有记录。相反,我想创建一个艺术家的新实例,并为这位新艺术家添加新的歌曲。
I have tried setting the primary key of artist to None before saving - and this forces a new instance of Artist. However, I lose the ForeignKey relationship between Artists and Songs.
我在保存之前尝试将艺术家的主键设置为无 - 这会强制一个新的艺术家实例。但是,我失去了艺术家和歌曲之间的ForeignKey关系。
Example View:
def edit(request, artist_id=None):
if artist_id == None:
artistsubmission = Artist()
else:
artistsubmission = Artist.objects.get(id = artist_id)
artistsubmission.pk = None
if request.method == 'POST':
form = ArtistEditForm(request.POST, instance=artistsubmission)
formset = SongFormSet(request.POST, instance=artistsubmission)
if form.is_valid() and formset.is_valid():
form.save()
formset.save()
return HttpResponseRedirect('/success/')
else:
form = ArtistEditForm(instance=artistsubmission)
formset = SongFormSet(instance=artistsubmission)
return render_to_response('edit.html', {'form':form, 'formset':formset})
4 个解决方案
#1
You can iterate over the individual forms in your formset, change what you need to and save.
您可以迭代formset中的各个表单,更改您需要的内容并保存。
if form.is_valid() and formset.is_valid():
artist = form.save()
for f in formset.forms:
song = f.save(commit=False)
song.artist = artist.id
song.save()
#2
I think the easiest way to do this would be to set default values by passing a dictionary in as the first argument to the form. You can get all of a model instance's fields by:
我认为最简单的方法是通过将字典作为第一个参数传递给表单来设置默认值。您可以通过以下方式获取模型实例的所有字段:
d_initial = Artist.objects.filter(pk=artist_id).values()[0]
This is the dictionary that you'd pass in to the form.
这是您传递给表单的字典。
form = ArtistEditForm(d_initial)
If you've excluded anything from the form, you might want to remove it from the dictionary. Same for id
. But this should produce a form that has all the values from the existing instance, but will save to a new instance.
如果您从表单中排除了任何内容,则可能需要将其从字典中删除。同样的id。但是这应该生成一个包含现有实例的所有值的表单,但会保存到新实例。
#3
I had a similar problem and I solved it like this:
我遇到了类似的问题,我解决了这个问题:
def edit(request, artist_id=None):
if artist_id == None:
artistsubmission = Artist()
else:
artistsubmission = get_object_or_404(Artist, id = artist_id)
if request.method == 'POST':
form = ArtistEditForm(request.POST, instance=artistsubmission)
formset = SongFormSet(request.POST, instance=artistsubmission)
if form.is_valid() and formset.is_valid():
artistsubmission.pk = None
artist = form.save(commit=False)
artist.id = None
artist.save()
for f in formset.forms:
song = f.save(commit=False)
song.artist_id = artist.id
song.id = None
song.save()
return HttpResponseRedirect('/success/')
else:
form = ArtistEditForm(instance=artistsubmission)
formset = SongFormSet(instance=artistsubmission)
return render_to_response('edit.html', {'form':form, 'formset':formset})
#4
I found this snippet which I think was more what you were looking for. http://www.djangosnippets.org/snippets/1246/
我发现这个片段我认为更符合您的要求。 http://www.djangosnippets.org/snippets/1246/
#1
You can iterate over the individual forms in your formset, change what you need to and save.
您可以迭代formset中的各个表单,更改您需要的内容并保存。
if form.is_valid() and formset.is_valid():
artist = form.save()
for f in formset.forms:
song = f.save(commit=False)
song.artist = artist.id
song.save()
#2
I think the easiest way to do this would be to set default values by passing a dictionary in as the first argument to the form. You can get all of a model instance's fields by:
我认为最简单的方法是通过将字典作为第一个参数传递给表单来设置默认值。您可以通过以下方式获取模型实例的所有字段:
d_initial = Artist.objects.filter(pk=artist_id).values()[0]
This is the dictionary that you'd pass in to the form.
这是您传递给表单的字典。
form = ArtistEditForm(d_initial)
If you've excluded anything from the form, you might want to remove it from the dictionary. Same for id
. But this should produce a form that has all the values from the existing instance, but will save to a new instance.
如果您从表单中排除了任何内容,则可能需要将其从字典中删除。同样的id。但是这应该生成一个包含现有实例的所有值的表单,但会保存到新实例。
#3
I had a similar problem and I solved it like this:
我遇到了类似的问题,我解决了这个问题:
def edit(request, artist_id=None):
if artist_id == None:
artistsubmission = Artist()
else:
artistsubmission = get_object_or_404(Artist, id = artist_id)
if request.method == 'POST':
form = ArtistEditForm(request.POST, instance=artistsubmission)
formset = SongFormSet(request.POST, instance=artistsubmission)
if form.is_valid() and formset.is_valid():
artistsubmission.pk = None
artist = form.save(commit=False)
artist.id = None
artist.save()
for f in formset.forms:
song = f.save(commit=False)
song.artist_id = artist.id
song.id = None
song.save()
return HttpResponseRedirect('/success/')
else:
form = ArtistEditForm(instance=artistsubmission)
formset = SongFormSet(instance=artistsubmission)
return render_to_response('edit.html', {'form':form, 'formset':formset})
#4
I found this snippet which I think was more what you were looking for. http://www.djangosnippets.org/snippets/1246/
我发现这个片段我认为更符合您的要求。 http://www.djangosnippets.org/snippets/1246/