Skip to content
Advertisement

Can’t handle two modelforms in a single submit/view flow with a FK relation?

I have the below two models

# models.py

class Applicant(models.Model):
    """
    A table to store all applicants, relates 1-n to an offer
    """
    name = models.CharField(max_length=50)
    job = models.CharField(max_length=50)
    start = models.DateField(null=True, blank=True)

    def __str__(self):
        return f'{self.name} applying for {self.job} starting {self.start}'


class Offer(models.Model):
    """
    A table to store created offers
    """
    # Relations
    applicant = models.ForeignKey(Applicant, on_delete=models.CASCADE)

    # Self
    monthly_raise = models.FloatField()
    months = models.PositiveIntegerField(validators=[MinValueValidator(1), MaxValueValidator(60)])
    start_salary = models.FloatField()

In my template I render all fields except for start (which I don’t render at all) in the same <form></form> wrapper. Now in my view I want to create new instances for each of the modelforms but only if both are valid.

This is what I have which throws

NOT NULL constraint failed: planner_offer.applicant_id

def render_dashboard_planner(request):

    site = 'planner'

    if request.method == 'GET':
        applicant_form = ApplicantForm()
        offer_form = OfferForm()

        context = {
            'applicant_form': applicant_form,
            'offer_form': offer_form,
            'site': site
        }

        return render(request, "dashboard/dashboard_planner.html", context)

    else:
        # Process the created Offer
        applicant_form = ApplicantForm()
        offer_form = OfferForm()

        form_applicant = ApplicantForm(request.POST)
        form_offer = OfferForm(request.POST)

        if form_applicant.is_valid() and form_offer.is_valid():

            # Grab the data
            form_applicant.save(commit=True)

            # Create Offer instance
            form_offer.save(commit=False)
            form_offer.applicant = form_applicant
            form_offer.save(commit=True)

        context = {
            'site': site,
            'offer_form': offer_form,
            'applicant_form': applicant_form,
        }

        return render(request, "dashboard/dashboard_planner.html", context)

How would I fix the relation issue and is this a proper way to handle the workflow in that manner at all?

Advertisement

Answer

You should set the .applicant on the .instance of the form, and use the instance of the form_applicant, not the form_applicant itself, so:

from django.shortcuts import redirect

def render_dashboard_planner(request):
    site = 'planner'
    if request.method == 'POST':
        form_applicant = ApplicantForm(request.POST, request.FILES)
        form_offer = OfferForm(request.POST, request.FILES)
        if form_applicant.is_valid() and form_offer.is_valid():

            # Grab the data
            applicant = form_applicant.save()
            form_offer.instance.applicant = applicant
            form_offer.save()
            return redirect('name-of-some-view')
    else:
        applicant_form = ApplicantForm()
        offer_form = OfferForm()

    context = {
        'applicant_form': applicant_form,
        'offer_form': offer_form,
        'site': site
    }
    return render(request, 'dashboard/dashboard_planner.html', context)
Advertisement