Irfan Harun
Irfan Harun

Reputation: 1059

how to save 3 forms of one model in django?

models.py

class Campaign(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    # this is many to one relationship, on_deleting user, profile will also be deleted
    funds_for = models.CharField(max_length=200)
    campaign_title = models.CharField(max_length=200, blank=True)
    amount_required = models.IntegerField(null=True, blank=True)
    campaign_image = models.ImageField(default="default_campaign.png",upload_to="campaign_pictures")

forms.py

class RaiseFundsFrom1(forms.ModelForm):
    class Meta:
        model = Campaign
        fields = ['funds_for']

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

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

views.py

@login_required
def raise_funds_medical_1(request):
    if request.method == 'POST':
        form = RaiseFundsFrom1(request.POST)
        if form.is_valid():
            request.session['funds_for'] = form.cleaned_data.get('funds_for')
            return redirect('funds:raise_funds_medical_2')
    else:
        form = RaiseFundsFrom1(instance=request.user)
        return render(request,'funds/raise_funds_medical_1.html',{'form':form})


@login_required
def raise_funds_medical_2(request):
if request.method == 'POST':
    form = RaiseFundsFrom2(request.POST, request.FILES or None)
    if form.is_valid():
        f = request.FILES['campaign_image']
        request.session['campaign_image'] = f.name
        return redirect('funds:raise_funds_medical_3')
else:
    form = RaiseFundsFrom2(instance=request.user)
    return render(request,'funds/raise_funds_medical_2.html',{'form':form})



@login_required
def raise_funds_medical_3(request):
    if request.method == 'POST':
        form = RaiseFundsFrom3(request.POST)
        if form.is_valid():
            request.session['campaign_title '] = form.cleaned_data.get('campaign_title')
            request.session['amount_required '] = form.cleaned_data.get('amount_required')
            c = Campaign()
            c.funds_for = request.session['funds_for']
            c.campaign_image = request.session['campaign_image']
            c.campaign_title = request.session['campaign_title']
            c.amount_required = request.session['amount_required']
            c.save()
            return redirect('core:landing_page')
    else:
        form = RaiseFundsFrom3(instance=request.user)
        return render(request,'funds/raise_funds_medical_3.html',{'form':form})

I need to get details for campaign model in 3 parts. I need to do in a specific order, with the second form requiring to be only an image upload. My plan is to capture the required fields and store temporarily in sessions. when the form is saved, i intend to delete the sessions(not important right now)

In the current scenario, when the third form is processed/saved. the contents of first form, third form and image name from second form gets saved. however, the image does not get uploaded to the required folder.

Upvotes: 0

Views: 140

Answers (1)

Chris
Chris

Reputation: 2212

I do not know whether it is possible to save a file to session with some trick. A workaround I see is to save the file in the required destination in the second view and save the path to that file in the session. Then use this saved path to create the object in the 3rd view.

So, create a small function to handle the upload

def handle_uploaded_file(f, upload_to):
    with open(upload_to, 'wb') as destination:
        for chunk in f.chunks():
            destination.write(chunk)

and then use it to save the file in your second view

def raise_funds_medical_2(request):
    if request.method == 'POST':
        form = RaiseFundsFrom2(request.POST, request.FILES or None)
        if form.is_valid():
            f = request.FILES['campaign_image']
            f_path_rel = os.path.join("campaign_pictures", f.name)
            f_path = os.path.join(settings.MEDIA_ROOT, f_path_rel)
            handle_uploaded_file(f, f_path)
            request.session['campaign_image'] = f_path_rel
            return redirect('raise_funds_medical_3')
    else:
        form = RaiseFundsFrom2(instance=request.user)
        return render(request,'funds/raise_funds_medical_2.html',{'form':form})

I think assigning the path to your image field should work, so that you do not need to change your 3rd view.

However, be aware of two things:

  1. The handle_uploaded_file expects that the correct destination folder exists. If you cannot guarantee this you should add a check for the folder and create it if not existant

  2. The handle_uploaded_file will overwrite exisitng files with the same name. If this should be avoided check for existing files before writing and modify the filename accordingly.

Upvotes: 1

Related Questions