In Django, how can I pre-populate the edit form from a model / child, but save the view as a new instance?

I am trying to pre-populate ModelForm and inlineformset_factory with a model instance, BUT, when the user submits the form, I need to create a new model instance and related child records.

Model Example:

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 editing form based on an instance of Artist, as well as an InlineFormSet for these artist related songs. The form will be pre-filled with existing data, and the user can change the artist name and song names. However, when the user submits the form, I do not want to overwrite existing entries. Instead, I want to create a new instance of Artist and add new songs for this new artist.

I tried to set the primary key of the artist to None before saving - and this forces a new instance of Artist. However, I am losing the ForeignKey relationship between Artists and Songs.

Example:

 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
source share
4 answers

You can iterate over individual forms in your form set, change what you need and save.

 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() 
+4
source

I think the easiest way to do this is to set the default values ​​by passing the dictionary as the first argument of the form. You can get all instances of a model instance:

 d_initial = Artist.objects.filter(pk=artist_id).values()[0] 

This is the dictionary that you will submit to the form.

 form = ArtistEditForm(d_initial) 

If you exclude something from the form, you can remove it from the dictionary. Same thing for id . But this should create a form that has all the values ​​from the existing instance, but saves it in the new instance.

+2
source

I had a similar problem and 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}) 
+1
source

I found this snippet, which I think was more than what you were looking for. http://www.djangosnippets.org/snippets/1246/

0
source

All Articles