Error saving file upload with ModelForm

Hello,

I’m having an issue saving a file upload with my ModelForm.
When I click the Save button after ADDING a new instance, it does not look like the ‘doc_file’ value is being set.

models.py

class Doc(models.Model):
    client = models.ForeignKey(Client, on_delete=models.CASCADE)
    name = models.CharField(max_length=100, default='')
    doc_file = models.FileField("File", upload_to="clients_docs", null=True, blank=True)
    category = models.CharField(max_length=50, choices=DocsCategoriesClient.choices, default='')

    class Meta:
        verbose_name = "Doc"
        verbose_name_plural = "Docs"

    def __str__(self):
        return str(self.name)

views.py

class DocAdd(UserAccessMixin, SuccessMessageMixin, generic.CreateView):
    permission_required = 'docs.add_doc'

    model = Doc
    form_class = DocForm
    template_name = 'clients/generic_add.html'
    success_message = model._meta.verbose_name + " added successfully."

    def get_context_data(self, **kwargs):
        context = super(DocAdd, self).get_context_data(**kwargs)
        client_id = self.kwargs['client_id']
        context['c_id'] = client_id
        context['client_full_name'] = get_object_or_404(Client, id=client_id)
        context['obj_name'] = self.model._meta.verbose_name
        return context

    def form_valid(self, form):
        print('in form_valid')
        doc = form.save(commit=False)
        client_id = form.data['hidden_client_id']
        client = get_object_or_404(Client, id=client_id)
        doc.client = client
        file_check = str(doc.doc_file)
        print('file check: ' + str(doc.doc_file))
        print('name check: ' + str(doc.name))
        print(self.request.POST)
        if file_check == "":
            form.add_error('doc_file', 'ERROR: The file was not uploaded. Please try again.')
            return super(DocAdd, self).form_invalid(form)
        else:
            return super(DocAdd, self).form_valid(form)

    def get_success_url(self):
        return reverse('clients:detail', args=[self.kwargs['client_id']])

forms.py (using crispy forms)

class DocForm(ModelForm):
	class Meta:
		model = Doc
		exclude = ['client']

	def __init__(self, *args, **kwargs):
		super().__init__(*args, **kwargs)
		self.helper = FormHelper()
		self.helper.layout = Layout(
			Fieldset('',
				'name',
			  	'doc_file',
			  	'category',
			),
			Hidden('hidden_client_id', '{{ c_id }}'),
			ButtonHolder(
				Submit('submit', 'Save'),
				HTML("<a href='{{ view.get_success_url }}' class='btn btn-light'>Cancel</a>")
			)
		)

generic_add.html template

{% extends "base.html" %}
{% block title %}Add {{ obj_name }}{% endblock %}

{% block content %}
    <div class="row">
        <div class="col-sm-12 col-lg-6">
            <h2>New {{ obj_name }} for {{ client_full_name }}</h2>

            {% load crispy_forms_tags %}
            <form action="" method="POST">
                {% csrf_token %}
                {% crispy form %}
            </form>
        </div>
    </div>
{% endblock content %}

When I fill out the form for a new Doc, and click the Save button, this is the output in my console:

[17/Mar/2022 09:35:10] "GET /clients/2/doc/add HTTP/1.1" 200 5315
in form_valid
file check:
name check: test
<QueryDict: {'csrfmiddlewaretoken': ['I_removed_for_clarity'], 'name': ['test'], 'doc_file': ['cute-dog.jpg'], 'category': ['Planning'], 'hidden_client_id': ['2'], 'submit': ['Save']}>
[17/Mar/2022 09:35:16] "POST /clients/2/doc/add HTTP/1.1" 200 5499

The page is returned to the same “add” page, and I receive the error message I created in the views.py file of: “ERROR: The file was not uploaded. Please try again.”

This means the line in views.py is not returning a doc_file value:
file_check = str(doc.doc_file)

Can you please help troubleshoot what I do not have right in my code?

Thank you in advance!

See the second paragraph at File Uploads | Django documentation | Django

You don’t have the enctype attribute set on your form tag.

1 Like

Can’t believe I forgot that simple step!
Thank you!

I’m just going to add enctype="multipart/form-data" to all the generic form templates, since it can’t heard to have it on all <form> tags.