cbuch1800
cbuch1800

Reputation: 943

Django image form isn't saving the image

I have a form that involves uploading a profile picture. I have it working so that I can upload images in the /admin/ interface and display them correctly, but I cannot get my Modelform to save the image.

Here is what I have:

models.py

class Candidate(models.Model):
    UserID = models.ForeignKey(User, on_delete=models.CASCADE)
    ElectionID = models.ForeignKey(Election, on_delete=models.CASCADE)
    Bio = models.CharField(max_length=500, blank=True)
    ProfilePicture = models.ImageField(upload_to="profilepics/", null=True, blank=True)

forms.py

class AddCandidateForm(forms.ModelForm):

    class Meta:
        model = Candidate
        fields = ['ElectionID', 'Bio', 'ProfilePicture']

cand_reg.html (Template)

{% block content %}
    <h1>Register as a candidate</h1>
    <form method="POST" class="post-form">
        {% csrf_token %}
        <h2>Select an election:</h2><br>
        {{form.ElectionID}}<br>
        <h2>Enter your bio:</h2><br>
        {{form.Bio}}<br>
        <h2>Upload a profile picture:</h2><br>
        {{form.ProfilePicture}}<br>
        <button type="submit">Register</button>
    </form>
{% endblock %}


When I try the view function like so I get the error:
MultiValueDictKeyError at /register/ "'ProfilePicture'"

views.py

def add_candidate(request):
    if request.method == 'POST':
        form = AddCandidateForm(request.POST, request.FILES)
        if form.is_valid():
            candidate = form.save(commit=False)
            candidate = request.FILES['ProfilePicture']
            candidate.UserID = request.user
            candidate.save()
            return redirect('/home/')
    else:
        form = AddCandidateForm()

    return render(request, 'cand_reg.html', {
        "form": form
    })

views.py

When I remove the offending line, the error goes away.

def add_candidate(request):
    if request.method == 'POST':
        form = AddCandidateForm(request.POST, request.FILES)
        if form.is_valid():
            candidate = form.save(commit=False)
            # candidate = request.FILES['ProfilePicture']
            candidate.UserID = request.user
            candidate.save()
            return redirect('/home/')
    else:
        form = AddCandidateForm()

    return render(request, 'cand_reg.html', {
        "form": form
    })

However, this doesn't actually save the image, so when I try to render it in a separate template, I get an error then.

Can anyone help me understand why the image isn't uploading?

Thanks in advance :)

Upvotes: 1

Views: 473

Answers (1)

nik_m
nik_m

Reputation: 12106

  1. You must set the ProfilePicture attribute of the model and not the instance itself (candidate = request.FILES['ProfilePicture']).

    Change to:

    candidate = form.save(commit=False)
    candidate.ProfilePicture = request.FILES['ProfilePicture']
    candidate.UserID = request.user
    candidate.save()
    
  2. Change your HTML form to accept files as well. Change to: <form method="POST" enctype="multipart/form-data" class="post-form">. When a form includes file inputs (<input type="file" />), then it must be encoded differently than it used when it includes only text. More here. If you right-click and inspect the {{form.ProfilePicture}} you'll see that this is actually a file input.

Extra one:

  1. Please, do not name your class attributes (ProfilePicture, UserID etc) in PascalCase. Use snake_case instead (profile_picture, user_id etc).

Upvotes: 2

Related Questions