Irfan Harun
Irfan Harun

Reputation: 1059

Django : Form Successful but image not uploaded

MODELS.PY

class Campaign(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    campaign_image = models.ImageField(default="profilepic.jpg",upload_to="campaign_pictures")

FORMS.PY

class RaiseFundsFrom3(forms.ModelForm):
    class Meta:
        model = Campaign
        fields = ['campaign_image']

VIEWS.PY

@login_required
def raise_funds_medical_3(request):
    if request.method == 'POST':
        form = RaiseFundsFrom3(request.POST, request.FILES or None, instance=request.user)
        if form.is_valid():
            check = form.save(commit=False)
            check.save()
            return HttpResponse('form worked')
    else:
        form = RaiseFundsFrom3()
        return render(request,'funds/raise_funds_medical_3.html',{'form':form})

URLS.PY

path('raise/medical/photo', views.raise_funds_medical_3, name="raise_funds_medical_3"),

raise_funds_medical_3.html

<form method="post" enctype="multipart/form-data">
  {% csrf_token %}
  <div class="form-group pt-2">
    <small>Photo formats must be PNG / JPG / JPEG</small>
    <input type="file" name="campaign_image" />
  </div>
  <button class="btn btn-lg button_bfg_blue" type="submit"> <small><b> NEXT  </b></small> </button>
</form>

on form submit, i do not get any error, but image is not uploaded to the required folder. however, in the raise_funds_medical_3 function within views.py, if i remove instance=request.user, the image gets uploaded but i get following error : NOT NULL constraint failed: funds_campaign.user_id

Upvotes: 0

Views: 961

Answers (2)

nigel222
nigel222

Reputation: 8192

Supplementary answer to dirkgroten's one

I have come to completely hate the conventional structuring of a Django Function-based View. They can be re-factored by inverting the validity test and adding one line so that one and only one instantiation of a form is present. The result is IMO far easier to read, and easily generalizes for a view displaying two or more forms.

def raise_funds_medical_3(request):
    args = [request.POST, request.FILES or None] if request.method == "POST" else []
    form = RaiseFundsFrom3(*args)
    if request.method != "POST" or not form.is_valid():
        # unbound form or form not valid
        return render(request,'funds/raise_funds_medical_3.html',{'form':form})

    # form is valid so do the processing and redirect
    check = form.save(commit=False)
    check.user = request.user
    check.save()
    return redirect(<url_pattern>)

If you want to process >1 form, the test becomes

    if request.method != "POST" or any(
        [ not form.is_valid(), not form2.is_valid(), ...]):

which forces evaluation of .is_valid() for all forms, even if the first was not valid, so that all the error messages are shown to the user.

In a complex business application, the processing of a successful form submission may be quite a few more lines of code than this simple example. Having it at the end, not indented, isolated from all the boilerplate save the return redirect(...), makes things much easier!

Upvotes: 0

dirkgroten
dirkgroten

Reputation: 20672

Your form is a ModelForm for a Campaign, so its instance needs to be a Campaign. Don't assign request.user as its instance!

Now, your form isn't including the user field which is required to save a Campaign, so you should assign that yourself in the view before saving to the database:

campaign = form.save(commit=False)  # this gives your the form's instance
campaign.user = request.user  # this assigns the user
campaign.save()  # this commits to the database

Also you should handle the case where the form isn't valid. This is quite simple, just un-indent the last return in your view function, so that return render(...) is also called in case the form isn't valid.

Finally, instead of returning a response when the form is valid, it's good practice to redirect to another view. This way, when the user refreshes the page, the form isn't submitted again. Your final code should look like this:

@login_required
def raise_funds_medical_3(request):
    if request.method == 'POST':
        form = RaiseFundsFrom3(request.POST, request.FILES or None)
        if form.is_valid():
            check = form.save(commit=False)
            check.user = request.user
            check.save()
            return redirect(<url_pattern>)
    else:
        form = RaiseFundsFrom3()
    return render(request,'funds/raise_funds_medical_3.html',{'form':form})

Upvotes: 3

Related Questions