Search code examples
pythondjangoinline-formsetdjango-formwizard

Django Form Wizard - Using an image formset for a related post model


So I'm using the Django Form Wizard to split a simplified PostForm. In one of the steps, visitors can upload different images related to the Post.

Within the done method for the SessionWizardView, I'm saving the instance first and then check for the images within the formset.

However I get the following error message;

save() prohibited to prevent data loss due to unsaved related object

I tried setting the related Post id for the formset but I'm missing something here, formsets are still something I can't really follow.. Any help is appreciated!

models.py

class Post(models.Model)
    title = models.CharField(max_length=200)
    description = models.TextField(max_length=1000)

    def __str__(self):
        return self.title 

class Image(models.Model):
    post = models.ForeignKey('Post', on_delete=models.SET_NULL, null=True)
    file = models.ImageField(upload_to='images/', null=True, blank=True)
    alt = models.CharField(max_length=200, blank=True)

views.py

FORMS = [
    ('title', PostCreateForm),
    ('image', ImageFormset)
]

TEMPLATES = {
    'title': 'post_form_title.html',
    'image': 'post_form_image.html'
}

class PostWizardView(SessionWizardView):
    form_list = FORMS
    file_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT, 'temp/'))

    def get_template_names(self):
        return [TEMPLATES[self.steps.current]]

    def done(self, form_list, form_dict, **kwargs):
        instance = Post()

        for form in form_list:
            instance = construct_instance(form, instance)
        instance.save() 
        post_id = instance.pk

        if 'image' in form_dict:
            formset = form_dict['image']
            if formset.is_valid():
                formset.save(commit=False)
                for form in formset:
                    form.post = post_id
                    formset.save()

forms.py

class PostCreateForm(forms.ModelForm):

    class Meta:
        model = Image 
        fields = '__all__'

ImageFormset = inlineformset_factory(
    Post,
    Image,
    form = PostCreateForm,
    min_num=1,
    can_delete=True
)

Solution

  • You should save the individual forms as you loop through them:

    if formset.is_valid():
        for form in formset:
            if form.is_valid():
                f = form.save(commit=False)
                f.post = post_id
                f.save()