ForeignKey Not returning linked data as expected

I have 2 models Project and Fundamentals the fundamentals is linked via FK to Project I have a view that allows me to add data to the fundamentals table and also a view to update the fundamentals when data already exists.

But when I try to retrieve the data i pass is the project_id in the request. But what seems to be happening is that if i have 5 projects and from project 5 i add fundamentals data the data in the table has an ID of 1 being that this is the first entry in the fundamentals table. but my project id is 5 being the 5th project

If i then go to project 1 and add fundamentals it will return data from project 5.

should i be sending in a different param in the request.
(request, project_name) ??

def fundamentals_view(request, project_id):
    check_exist = Fundamentals.objects.filter(project_name_id=project_id)
    if check_exist:
        return update_fundamentals_view(request, project_id)
    else:
        return add_fundamentals_view(request, project_id)


@login_required
def add_fundamentals_view(request, project_id):
    project = get_object_or_404(Project, pk=project_id)
    if request.method == 'POST':
        form = FundamentalsForm(request.POST)
        if form.is_valid():
           fundamental = form.save(commit=False)
           fundamental.project_name = project
           fundamental.save()
           return redirect('dashboard.html')
    else:
        form = FundamentalsForm()
    return render(request, 'pages/add_fundamentals.html', {'project': project, "form": form})


def update_fundamentals_view(request, project_id):
    project = get_object_or_404(Fundamentals, pk=project_id)
    form = FundamentalsForm(request.POST or None, instance=project)
    if form.is_valid():
        form.save()
        return redirect('/')
    return render(request, 'pages/update_fundamentals.html', {'project': project, "form": form})

Hi TommyQuality,

From a general approach, I would recommend removing the function fundamentals_view as a proxy of calling add or update. When rendering the template, the app should know if the action should be an add or an update. In the add view, you can verify that one indeed doesn’t exist before creating it for a project and redirect them to the update page. However, you don’t have to do this.

I suspect something is wrong here. The get_object_or_404 is operating on the Fundamentals model, but the variable is named project. Additionally, the project_id being passed in was used to fetch the Project instance in fundamentals_view. I would assume you want this to be:

fundamentals = get_object_or_404(Fundamentals, project_name_id=project_id)

However, if that’s a foreign key from Fundamental to Project, then that will error when there is more than one Fundamental for a Project. If there should only ever be one, you should consider using a OneToOneField for the relationship. If there should be more than one, you’ll need to change your approach to specify an unique identifier for the Fundamental that should be updated or otherwise determine which Fundamental instance to update (such as most recently created).

Tim, in the category of “How did we get to this point?”, see Check if record exists in model :slight_smile:

Thanks CodenameTim,

There should only ever be 1 entry in fundamentals for a project.

I have a FK from fundamental to project
project_name = models.ForeignKey(Project, on_delete=models.CASCADE)

What i dont understand is if i am querying project_id , is that the ID of the entry in the Project model which would be created based on a first come first serve. So how will the Project.ID ever relate to the Fundamental.ID unless both entries are created at the same time?

Or is there something happening in the background that knows what project.id relates to which fundamental.id?

You are creating a ForeignKey relationship, not a One-to-One relationship. The ID fields of those two tables have no relationship to each other.

Your link between the two is the ForeignKey field of the Fundamentals table.

This means that, given a Fundamentals object named fundamental, your access to the Project object is fundamental.project.

Again, since this is an FK relationship, your access to all the related Fundamentals object from a Project object is project.fundamentals_set.all(). If you just want one object (since there should only be one even thought the database structure allows otherwise), then you could use project.fundamentals_set.all().first() Or, if you have the Project’s id value as id_value, you could query it as Fundamentals.objects.filter(project_name_id=id_value)

Thanks Ken.

Im sure this is really simple stuff. but jeez i am confused.

This line:
check_exist = Fundamentals.objects.filter(project_name_id=project_id)

Is that checking that the project_name_id in the fundamentals model (which is the ID of the entry in fundamentals ??) equals the project_id past in the request exists?

But it can’t be, because it works, but the project_id being past in via the request is 1 and the entry in fundamentals is 7.

So i think i am misunderstanding this project_name_id

You have a field in Fundamentals called project_name. It is the FK to Project. In reality, the database column name (visible to Django) is project_name_id.

So, assuming that the project_id being supplied is 1, this query is answering the question:
What are all the Fundamental objects that exist, that are referring to the Project object with an id of 1?
It has absolutely nothing to do with the ID field of the Fundamental object itself.

Ok.

So that is why Tim is saying this line is incorrect
project = get_object_or_404(Fundamentals, pk=project_id)

because that is checking that project_id exists when it should be project_name_id=project_id

which will get the the project_id past in via the request matching the ID in the project_name_id column?

Its my misunderstanding of this project_name_id that has ruined me.

Thanks both for your help. Ill try not to bother you again

You’re on the right track - but follow this line of thinking a little bit further.

Why do you think a get_object_or_404 is the right call here? What are you trying to protect against? And is it necessary to do that if you’ve gotten to this point?

Also, I strongly suggest you change your variable names to properly represent the type of object being processed.

get_object_or_404 Why would this be wrong? Is this doing more than getting the object if exists or returning a 404.

I assume you are saying that returning a 404 response is unnecessary at this point in the user journey?

So would just using get() be better?

I now understand more about my views and will definitely rename the variables to reflect the data.

I don’t think I’d say it’s wrong as much as I would describe it as potentially-confusing. It may lead someone looking at this code to drawing an incorrect conclusion about this function. (Then again, it may not.)

Nope, that’s exactly what it’s doing.

That is an opinion I have, yes. (That’s all it is, just an opinion.) It’s not a question of correctness. It’s an issue of code-clarity and obviousness.