How to get PK from forms submission to set FK

I have 3 models: Human, Human_notes, and Location; creating 3 forms. All three are in one template as a ‘single form’ submitted at one time. I am trying to get the given primary key values from 2 of the form submissions and use these as the foreign keys on the 3rd form. BUT this does not work the way I am doing it… why? Is this the general idea of how you would do this? I get an error saying:
‘LocationForm’ object has no attribute ‘id’ though I have explicitly set that in the form and models.

oh, and the other form data IS saved… just no linking keys.

Thanks in advance…

Here is my template:

        <form action="human-add" method="POST">
            {% csrf_token %}
            {{ human_form.as_p }}
            {{ location_form.as_p }}
            {{ human_notes_form.as_p }}
            <button type="submit" class="save btn btn-success">Save</button>
        </form>

Here is my view:

def human_add(request):
    if request.method == 'POST':
        human_form = HumanForm(request.POST)
        location_form = LocationForm(request.POST)
        human_notes_form = HumanNotesForm(request.POST)

        if human_form.is_valid() and location_form.is_valid() and human_notes_form.is_valid():
            human_form.save(commit=False)
            location_form.save()
            locationKey = location_form.id  
            human_notes_form.save(commit=False)
            human_notes_form.intaker = request.user
            human_notes_form.save()
            noteKey = human_notes_form.id
            human_form.location = locationKey
            human_form.note = noteKey
            human_form.intaker = request.user 
            human_form.save()
            return redirect('/intake/success-form') 
        else:
            context = {
                'human_form': human_form,
                'location_form': location_form,
                'human_notes_form': human_notes_form,
            }
    else:
        context = {
            'human_form': HumanForm(),
            'location_form': LocationForm(),
            'human_notes_form': HumanNotesForm(),
        }
    return render(request, 'intake/human-add.html', context)

Here is the Human model piece:

class Human(models.Model):
    intaker = models.ForeignKey(User, default=None, on_delete=models.CASCADE, null=True)
    location = models.OneToOneField('Location', on_delete=models.CASCADE, null=True,   related_name='humans')

Here are my forms:

class HumanForm(forms.ModelForm):
    class Meta:
        model = Human
        widgets = {'pwd': forms.PasswordInput(),'roles': forms.SelectMultiple(attrs={'size': 2}),}   
        fields = ['intaker','roles','fnm','lnm','phn1','phn1_txt','phn2','phn2_txt','eml1','eml2','pwd','rcvr_code','alt_fnm','alt_lnm','alt_phn','cart_id','auth_care','img','img_id']  #"__all__"

class HumanNotesForm(forms.ModelForm):
    class Meta:
        model = HumanNotes
        fields = ['id','intaker','note'] 

class LocationForm(forms.ModelForm):
    class Meta:
        model = Location
        fields = ['id','st1','st2','cty','county','state','zip','lat','long','img','img_nm','img_id'] 

wazup?

The location_form doesn’t have an ID field. The Location model has the ID field.

You’re looking to do something more like:

this_location = location_form.save()
locationKey = this_location.id

See the save() method API docs, and don’t let yourself get confused between a model and the form that provides a UI allowing you to make changes to that model. (For example, you can create forms that aren’t associated with models. You can create forms that let you enter data to be sent elsewhere.)

Also note that once saved, a model form has an instance attribute named, appropriately enough, instance, allowing you to do something like this:

location_form.save()
locationKey = location_form.instance.id

Ken

Ken,
Many thanks for the very concise and well written reply. I really appreciate the presentation of an idea to research and learn vs just the step by step fix. “Give someone a fish and feed them for a day, TEACH a person to fish and feed them for their life”… :wink: The distinction of the model vs form clarification was key (pun intended).

I THINK this is what you mean:

def human_add(request):
    if request.method == 'POST':
        human_form = HumanForm(request.POST)
        location_form = LocationForm(request.POST)
        human_notes_form = HumanNotesForm(request.POST)
        if human_form.is_valid() and location_form.is_valid() and human_notes_form.is_valid():
            loc = location_form.save()
            hum = human_form.save(commit=False) 
            humnote = human_notes_form.save(commit=False)
            hum.intaker = request.user
            hum.location = loc.id
            human_form.save()
            humnote.intaker = request.user
            humnote.human = hum.id
            human_notes_form.save()
            return redirect('/intake/success-form') 
        else:
            context = {
                'human_form': human_form,
                'location_form': location_form,
                'human_notes_form': human_notes_form,
            }
    else:
        context = {
            'human_form': HumanForm(),
            'location_form': LocationForm(),
            'human_notes_form': HumanNotesForm(),
        }
    return render(request, 'intake/human-add.html', context)

However, I get a " ValueError at /intake/human-add
Cannot assign “16”: “Human.location” must be a “Location” instance." So I think I am getting closer as 16 should be the PK for the new human. The Human model has this for the location field: " location = models.OneToOneField(‘Location’, on_delete=models.CASCADE, null=True, related_name=‘humans’)"

Thoughts?

Ah and duh: if it wants the instance, give it the instance…changed to this and I think it worked!

def human_add(request):
    if request.method == 'POST':
        human_form = HumanForm(request.POST)
        location_form = LocationForm(request.POST)
        human_notes_form = HumanNotesForm(request.POST)

        if human_form.is_valid() and location_form.is_valid() and human_notes_form.is_valid():
            print("All forms validation passed")
            loc = location_form.save()
            hum = human_form.save(commit=False)  
            humnote = human_notes_form.save(commit=False)
            hum.intaker = request.user
            hum.location = loc   #.id  COMMENTED OUT SPECIFIC FIELD VALUE
            human_form.save()
            humnote.intaker = request.user
            humnote.human = hum    #.id COMMENTED OUT SPECIFIC FIELD VALUE
            human_notes_form.save()
            return redirect('/intake/success-form') 
        else:
            print("Problem with a form!")
            context = {
                'human_form': human_form,
                'location_form': location_form,
                'human_notes_form': human_notes_form,
            }
    else:
        context = {
            'human_form': HumanForm(),
            'location_form': LocationForm(),
            'human_notes_form': HumanNotesForm(),
        }
    return render(request, 'intake/human-add.html', context)

It DID!!! IT DID!!! Thanks!!!

1 Like