Django Form Wizard not saving FileField

437 Views Asked by At

I have a 5 step form wizard, each their own form. I have a FileField in the first, but something is going wrong. When I reach the final step and press submit, my model gets saved but the file field is empty in my database. It's not "null", but actually empty. I created the "file_storage" variable but that only stores the file temporarily, it get's deleted as soon as I submit the form

this is my wizard class:

import json
import logging
import os

import keyboard
from django.conf import settings
from django.core.files.storage import FileSystemStorage
from django.core.mail import EmailMessage
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from formtools.wizard.views import SessionWizardView

from .forms import *

logger = logging.getLogger('django')

FORMS = [
    ("company", CompanyForm),
    ("addresses", AddressesForm),
    ("references", ReferenceFormSet),
    ("contacts", ContactFormSet),
    ("payment", PaymentForm),

]

TEMPLATES = {
    "company":    "form/step-1.html",
    "addresses":  "form/step-2.html",
    "references": "form/step-3.html",
    "contacts":   "form/step-4.html",
    "payment":    "form/step-5.html",

}

form_data = []
form_data2 = {}

class RegisterWizard(SessionWizardView):
file_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT, 'temp'))

def get_context_data(self, form, **kwargs):
    context = super().get_context_data(form=form, **kwargs)
    context.update({'callme_forms': CallMeForms(prefix='callme')})

    return context

@property
def first_step_files(self):
    return self.storage.get_step_files(self.steps.first)

def process_step(self, form):
    data = {}
    form_data.extend(self.get_form_step_data(form))
    for attr, value in self.get_form_step_data(form).items():
        if 'reference' not in attr:
            if 'company-' in attr:
                attr = attr.replace('company-', '')
            if 'addresses-' in attr:
                attr = attr.replace('addresses-', '')
            if 'payment-' in attr:
                attr = attr.replace('payment-', '')
            if 'document-' in attr:
                attr = attr.replace('document-', '')
            if value == 'on':
                value = True
            if value == 'off':
                value = False

            data[attr] = value if value else data.get(attr)

        if 'reference' not in attr or 'contact' not in attr:
            try:
                form_data2.update(data)
            except e:
                logger.error(e)
    return self.get_form_step_data(form)

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

def render_goto_step(self, *args, **kwargs):
    form = self.get_form(data=self.request.POST, files=self.request.FILES)
    self.storage.set_step_data(self.steps.current, self.process_step(form))
    self.storage.set_step_files(self.steps.first, self.process_step_files(form))

    return super().render_goto_step(*args, **kwargs)

def done(self, form_list, **kwargs):
    data = {}
    data2 = {}

    form_data2.pop('csrfmiddlewaretoken')
    form_data2.pop('register_wizard-current_step')

    try:
        data2.update(form_data2)

        for k, v in form_data2.items():
            if 'reference' in k:
                data2.pop(k)
            if 'contact' in k:
                data2.pop(k)

        form_data2.clear()
        form_data2.update(data2)

        form_data2.pop('wizard_goto_step')

        if 'using_mail_address' in form_data2:
            form_data2.pop('using_mail_address')
        if 'different_invoice_address' in form_data2:
            form_data2.pop('different_invoice_address')
        else:
            data['invoice_street'] = form_data2.get('delivery_street')
            data['invoice_zip'] = form_data2.get('delivery_zip')
            data['invoice_city'] = form_data2.get('delivery_city')
            data['invoice_house_number'] = form_data2.get('delivery_number')
            form_data2.update(data)

        form_data2.pop('toa')
        instance = Customer()
        customer = Customer.objects.create(**form_data2)

        form_data_sets = [form.cleaned_data for form in form_list]

        for form in form_list[2].save(commit=False):
            form.customer_id = customer.id
            form.save()

        for form in form_list[3].save(commit=False):
            form.customer_id = customer.id
            form.save()

        form_data2.clear()
        kwargs.clear()
        Customer.objects.all().none()
    except e:
        logger.error(e)
    finally:
        return HttpResponseRedirect('/thank-you')`

forms.py:

class CompanyForm(ModelForm):
 ...
 file = forms.FileField(
    label=_("Choose File"),
    widget=forms.ClearableFileInput(attrs={'class': 'form-control right', 'placeholder': 'choose a file'}),
    required=False,
)
 class Meta:
 ...

models.py:

class Customer(models.Model):
 ...
 file = models.FileField(
    verbose_name='File',
    upload_to='uploads/',
    null=True
)

No errors, no warning messages. I'm lost...

2

There are 2 best solutions below

0
On

found out that in form_data_sets = [form.cleaned_data for form in form_list] the file is actually located in there. So I just did:

file = {'kvk_file': form_data_sets[0].get('kvk_file')}

form_data2.update(file)

and now it works!

0
On

I would say use Instance of Model like below

from formtools.wizard.views import SessionWizardView
from django.core.files.storage import FileSystemStorage
from django.conf import settings
from django.shortcuts import redirect
from .models import Customer

class RegisterWizard(SessionWizardView):
    instance = None
    file_storage = FileSystemStorage(location = settings.MEDIA_ROOT / 'temp')

    def get_form_instance(self, step):
        """
        Provides us with an instance of the Project Model to save on completion
        """
        if self.instance is None:
            self.instance = Customer()
        
        return self.instance

    def done(self, form_list, **kwargs):
        """
        Save info to the DB
        """
        # Add
        customer = self.instance
        customer.created_by = self.request.user
        customer.save()

        return redirect('/thank-you/')