Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Hint: You can adjust the default video playback speed in your account settings.
Hint: You can set your subtitle preferences in your account settings.
Sorry! Looks like there’s an issue with video playback 🙁 This might be due to a temporary outage or because of a configuration issue with your browser. Please refer to our video player troubleshooting guide for assistance.

Variable Arguments From URLs: Routing and Views

In the previous lesson, you learned how to pull one specific resource out of your database. Now you need to build that code into your views and set up the correct routing so that you’ll be able to see the details page of one of your projects.

In urlpatterns in your projects app, you had an empty path that just went to projects without anything afterwards and then rendered your all_projects view. Now, you need to add a second path:

Python
urlpatterns = [
    path('', views.all_projects),
    path('<int:pk>', views.project_detail)
]

You can use the characters < and > to captures a part of a URL and pass it forward into your app.

00:00 In the previous video, we learned how to pull one specific resource out of our database. Now, we want to build that code into our views and set up the correct routing so that we’re able to see this details view of one of our projects.

00:17 Over here in the urlpatterns of our projects app—remember, we have this empty path that just goes to /projects/ without anything afterwards—and then renders our all_projects() view, which lists all the projects that we have.

00:33 Now, we want to add a second path() in here.

00:38 This might look a bit weird at the beginning, but just take a look. We’re going to talk over what’s going on, okay?

00:55 All right. So, PyCharm’s complaining, project_detail—this view doesn’t exist yet. We already know what that’s going to bring us, it’s going to bring us a little error, but we can build this view.

01:07 We did it before and we’re going to do it again, but the interesting part for us now is this part here.

01:15 So, that looks weird, right? We don’t have just some text in there, but we have these angled brackets. We have a : in there. int looks familiar, could be integer, right? pk—we just talked about that before.

01:25 Maybe it’s the primary key. But what exactly is going on in here? Let’s take a look at this on some slides! So, what we want to do with these angled brackets is we want to use them to capture parts of the URL.

01:40 So, you can imagine this little cowboy emoji that has this lasso with these angled brackets. It uses it to fetch stuff from the URL and pass it forward into our app.

01:51 So, what you can see here is essentially what—in the demo, before—I typed into the browser bar up top, right? We were on /projects/, and then we could just type the different numbers—in this case, 1and then it brought us to this details page.

02:07 What’s happening here is that our cowboy emoji lassos the rest of the URL—the number, in this case—with the angled brackets, and then flips it up there and passes it on to our view, whatever we defined here—in this case, project_detail().

02:26 Remember that how it’s passing it forward—in that case, that’s defined over here. We’re giving it a variable named pk, which just stands for primary key.

02:36 But this is going to be available inside of project_detail()—in this view function—under the variable pk, okay? That’s important to remember for us to then build out the view that we need to correctly render this details page. All right, so with this, we explained the angled brackets here and this pk, right?

02:57 But what’s this int and the : here? This part is called the path converter. There’s different ones. In this case, we’re using the int one with a :.

03:08 That’s a path converter. What our integer path converter does is it makes sure that what gets entered here at the end is actually an integer. Otherwise, it gives us an error.

03:18 So it avoids that some funny guy would come in here and—instead of just giving a number at the end—type some funny URL, right? This is just not going to work because the path converter checks whether this is an integer—what gets cut off here, the rest of this. And if it isn’t, then it just says, “Okay, I’m not passing this forward.” This avoids that we would run into an error later, because we need the primary key to be an integer to correctly query our database. We wouldn’t find anything here.

03:51 This catches it early and just tells us, “Nope, this is not going to work.” And something like this comes in, a 1, then this can be converted to an integer, and it’s going to give it a go-ahead and pass 1 forward to project_detail(), to the function. Inside of the function, then, we’re going to use it to query our database for this very specific entry that we’re looking for. Next, let’s head over to views and build out the code logic that we’ve just been talking about. So, heading over to views, we now want to create a second view function.

04:30 We have all_projects() here, and here, our error friend is already complaining that 'project_detail' doesn’t exist, so that’s what we’re going to build next.

04:41 project_detail() takes a request as an input. Then now, we’re at an interesting stage because it’s also going to take a primary key as an input.

04:51 It’s going to take an integer as an input that gets sent over by our URL resolver. So this part here, as you remember, the little cowboy catches it and passes it on to project_detail().

05:05 So, it’s coming in here as a second argument.

05:10 Okay. And that means we can use it, right? We want to query for one Project, so I’m going to say project = Project.objects.get(), and this code looks familiar because it’s exactly what we’ve been using inside of the Django shell, before, to query our database.

05:29 We’re doing the exact same thing and we’re giving it, here, the number that comes from the URL. So if a user puts in a 1, it’s going to pass forward the 1 here and query our database for the first entry. If it’s a 2, for the second one, et cetera.

05:46 Cool! Then, all we need to do is return this. We’re going to want to render a template again.

06:03 And again, we’ll need to pass this context dictionary in here, and here, I’ll just call it 'project' because we’re only passing one Project.

06:14 We hand it forward in the same way that we handed the list of projects forward before. All right. This finishes up our view function. Let’s go over it quickly again.

06:26 We’re defining project_detail(). We’re passing in the request—as always, in Django. In this case, what differentiates it from the other one before is from our urls file, we’re getting the information that it cuts off from the URL and passes forward to project_detail(). We’re catching it here, and then we’re using it in order to be able to query our database, to get a very specific Project back from it.

06:53 The rest is the same as above: we’re redirecting forward to a template and we’re passing this project, the Project object that we pulled out of the database so that we can then later render it in our template. Let’s look at this in an overview.

07:15 Here we go. Inside of our browser, someone enters this /projects/1, for example. This part gets snipped off by the path()—by using these angular brackets,

07:29 it cuts it off. It checks: is it an integer? In this case, yes. And passes it forward under this variable name to our new view function. In there, we use it to query the database and finally return the object that we got from there and pass it forward to the template.

07:47 And remember, yeah, the one who’s doing this using the angled bracket lasso is our cowboy emoji. All right. So, is that it? Can we now access a single resource? Let’s give it a try.

08:01 I’m here at our page. We wanted to try, “Can I access the first resource if I type this in there?” All right! We’re getting somewhere. We’re getting a familiar error, it’s telling us TemplateDoesNotExist. Right. We said, “Forward to detail.html,” but we don’t have this template yet. So let’s go ahead, follow Django’s tip, and build out this template.

08:42 Okay. So, I have this basic structure again. Inside of the body I’m just going to try to print out if this—can we access our Project object?

08:54 Let’s take a look.

08:58 And there it is! Perfect. So, it’s working. And now, if I go to the second one, there we are. We’re finding the second Project object. I’m going to make this a bit more readable real quick, but this is already a success for us.

09:37 So, let’s use this also to revisit how we can display an image from a static file. We need an img tag here that links to a static tag,

09:50 static, and then give the URL here: project.image. That’s the path that actually finds its way in here to our image file, the associated one with this project.

10:06 And here, we can give in the .description.

10:13 And what else we need to do is at the beginning of the template, we need to {% load static %}.

10:24 Okay? Now, when I head over here and reload, we can also see the image. We have the image, we have the title, the description. We can also add the technology. Let’s finish it up.

10:47 And now, we have all the information from our database displayed here on the details page. I’m now on number 2, but I can easily switch to number 1 and I get all the different information here. All right, great! With this code, we built out the functionality to actually display a single resource.

11:06 We already picked out some of the information that we want to display from the database. In the next video, we’re going to take a look how to link this

11:17 from this standard page so that we can then click, actually, instead of having to type in the URL ourselves up here. See you there.

eclaudio on Dec. 4, 2019

i love your tutorails!!!

eclaudio on Dec. 4, 2019

I love your tutorials!!!

that last message had terrible grammar.

Martin Breuss RP Team on Dec. 4, 2019

Haha, thanks so much @eclaudio :) Django on tuto-Rails!

hollowde on Jan. 1, 2020

I am getting the error

local variable ‘project’ referenced before assignment and thus my links are not working

hollowde on Jan. 2, 2020

I am getting the error

“local variable ‘project’ referenced before assignment”

when I do the projects/1 or projects/2

thus my links are not working

what would you recommend that I do to correct the error I am seeing

Martin Breuss RP Team on Jan. 2, 2020

My quick guess is that you might have spelled Project in the line:

project = Project.objects.get(pk=pk)

with a lowercase p. Keep in mind that you’re assigning the result of the database call Project.objects.get(pk=pk), which uses the Project class from models.py, to a new variable called project (lowercase!), which you are then using.

If the model-reference to the Project model is accidentally in lowercase, you could get the error you are seeing.

Richard Childers on Jan. 23, 2020

Hi Martin,

I am working through your course. Like it very much. Noticed one thing that caused by a bit of problem. In the video called Variable Arguments From URLs: Routing and Views you show a resolving urls summary with the function project_detail with its return statement as so:

return render(request, "projects/detail.html", **{"projects":project})** 

The highlighted part should be {project.project}). If I use projects, plural I get 404 error. You have it correct earlier in the video.

Great work Enjoying the video.

All the best

Richard

Martin Breuss RP Team on Jan. 24, 2020

@Richard Childers: thanks for noticing and posting this clarification here! 🙌

Indeed, in the summary slide, from 7:18 forward in the video, please note that the last line in views.py should be:

    return render(request, "projects/detail.html", {"project": project})

I’m almost through this course and am trying to modify everything to suit my side-project, which is the create a webpage with the following structure…

Home page - this will just be menu with 3 options to select leading to 3 different pages): ** 1) Projects ** -> From this Projects page, could further drill into project detail pages for each respective project. This page will pull my project details from the JIRA application using api. ** 2) Production Processes -> Would be mostly informational/text at first, but eventually would like to build an interactive calendar application on this page. ** 3) Modeling Applications -> This page will mostly be informational/text

Given this information, is there any particular way I should structure my django project? For example, do I keep everything under my “Home” app folder, or should I be creating separate “applications” for each of these pages?

And if you have any good information or tutorials on best practices for structuring django projects, please feel free to pass along.

Many thanks!

TC on Feb. 1, 2020

So I just tried creating all pages nested under the Home folder. For example, I had the following pages/structure working:

Home -> Projects -> Project# Details

I then proceeded to link the button between Projects page -> Project # Details page. This also worked (by setting app_name=’project’, as you show in your tutorial).

But now I have a conflict…I cannot also create the link between the different Sections on the Home page to the actual section details. The only way that I can correctly link from Home page, say, to the Projects page is if I change the app_name, but this of course breaks the Project -> Project # Details linkage since there can only be one app_name variable. So, maybe I will just wait to get your feedback on my comments above as I’m clearly not structuring this correctly :)

Martin Breuss RP Team on Feb. 2, 2020

Hi @TC! Nice work on extending the app to fit your personal project. Best way to learn more 🙌.

Project Structure

I would suggest to split the functionality you described into three different apps, instead of putting them all into one home app. That means your Django project will contain three Django apps:

  1. projects
  2. processes
  3. blog

Of course you can use different names, whatever describes the apps best. I chose them from your description, with the assumption that the third one will be mostly technical writing you would do.

That way you keep the different functionality in different apps.

Page Linking

If you follow the suggestion above, you can have separate app_names for each of the apps, and link to the apps e.g. like so:

{% url 'projects:details' %}
{% url 'processes:overview' %}

The two snippets above assume that you have two apps (projects and processes) that both have their app_name defined. Additionally, it requires that the projects app has a details function in the app’s views.py, and the same for an overview function in the other app.

Page Linking With One App

Even without following the suggestion above and sticking with your current app setup, you should be able to link to different view functions when they are in the same namespace. This could look like so:

{% url 'projects:details' %}
{% url 'projects:overview' %}

Note that you need to define separate view functions and also link to them specifically. One of the lines above would resolve to a URL that points to the details function, the other one to the overview function. In this case, both of them are located in the views.py file of the same app–one with the namespace (=app_name) projects.

Does this help to clarify the situation?

TC on Feb. 3, 2020

Ah, perfect! Thanks so much, Martin. I’ll attempt to do it by what seems to be the more correct way and split them into separate apps.

Thanks again!

Hi Martin - just getting some time recently to try your recommendations out. I structured things slightly differently in that I created 4 apps instead of 3:

1. Home - Desc: this app will serve two main purposes: 1) Will show a summary view of my workday and to-do’s, and 2) Will house 3 icons which will link to the 3 apps below.

2. Projects 3. Processes 4. Applications

So, with the “{% url ‘....’ %}” lines you wrote your previous comment, obviously, there are multiple of them now since we need one for each app and its respective namespace. And those lines of code will reside in home->templates->home->html file. Then, it seems I will need to dynamically provide the namespace and function for the given app in this html file?

If so, would you then recommend that I just store the namespace and corresponding function names in the home app database and plug it into the “{% url ‘....’ %}” line of code dynamically usign the for loop you previously showed us how to write? Or is there a different or more proper way I should be doing this?

Trying to do things semi-correctly before I get too far down the road with this app and then realize I have to do a major overhaul.

Martin Breuss RP Team on Feb. 21, 2020

Nice job following the double-folder structure! 👍

home -> templates -> home -> something.html

^ This is the right place to put your HTML files for your Home app. :)

Then, from what I understand, you want to link to the main pages of your other apps from the Home app, right?

Just as you wrote, you will need to include the {% url app_name:name %} as the href attribute for each link that you’re creating.

There’s no need to use a loop for that and I think that would over-complicate things. You already know what are the three pages that you want to link to (= the base pages of your three apps), so you can put in three links with the corresponding URL patterns, like so:

<a href="{% url projects:name_of_your_view_function %}">Projects</a>
<a href="{% url processes:name_of_your_view_function %}">Processes</a>
<a href="{% url applications:name_of_your_view_function %}">Applications</a>

You will need to replace the name_of_your_view_function part with whatever names you gave the main functions of your different apps in the respective urls.py files.

Martin Breuss RP Team on Feb. 21, 2020

Also check out the next video which talks in more detail about URL linking in Django.

ehaglund62 on May 15, 2020

When I runserver to 127.0.0.1:8000/projects/1 I get an error message saying “Invalid block tag on line 8: ‘static’. Did you forget to register or load this tag?”

I’m pretty stuck on this. I’m not sure where, or how far back the error goes.

Martin Breuss RP Team on May 16, 2020

@ehaglund62 Sounds to me like you might have forgotten a piece for setting up your static files correctly. Your template needs two things for that:

  1. Loading the static tag: This happens at the top of each template where you want to reference static files: {% load static %}
  2. Linking the static file: With static loaded, you can then use the static template tag to link to CSS files or images etc.: <img src="{% static 'your/path/image.png' %}">

Check out Django’s helpful docs on Managing static files.

ehaglund62 on May 16, 2020

@Martin Breuss, I can’t believe I missed that! Thanks for the fix.

vivekdeo on May 25, 2020

view:

def project_detail(request,pk):
    project = Project.objects.get(pk=pk)
    print(project)
    return render(request, 'projects/detail.html',
                  {'projects': project})

detial.html:

/head>
<body>
 <h1>Inside detail.html!!</h1>
 {{ project }}
 <p>{{ project.title }}</p>
</body>
</html>

When I run http://127.0.0.1:8000/projects/1 I can see the message Inside ..... on web page , but it nodes not show anything for {{ project }}

print project on server command line shows:

Project object (1)

What am I missing?

Martin Breuss RP Team on May 26, 2020

Hi @vivekdeo you’ve stumbled across a subtle typo: one superfluous "s"!

In your view function you’re passing the data on to your template with the key name projects. This key name is how you will be able to access it in your template. However, in your template you are trying to access the data via the key project (without the s at the end).

So you just need to keep in mind that the key in your context dictionary and the variable name that you use to access the data under in your template match up.

tomislavm021 on Aug. 5, 2020

What if you have more registered users, who want to add stuff in their app?

How would you filter those results so only users who created them, can see them?

I am trying with .filter but i can not get it to work :(

charneuk on Aug. 11, 2020

When I load 127.0.0.1/8000/projects/1, I get a blank screen.

charneuk on Aug. 11, 2020

NM. I was using {'projects':project} in the views.py file. Changed it to project. Now it works.

David K on March 31, 2021

Proofreading assist :) Slide Page 245 of PDF (and your video), refers to projects rather than the singular project in the reference passed into the detail view.

The video of the coding process is correct, but the slides shown (and the PDF) is incorrect. Just in case someone was following the slides…

David K on March 31, 2021

Slide content pp 245

return render(request,"projects/detail.html", {"projects": project})

should be

return render(request,"projects/detail.html", {"project": project})

Martin Breuss RP Team on March 31, 2021

Hi @davidcfk. Yes, that’s a typo! There are some comments further up clarifying that already, but thanks for bringing it up again to have it at the bottom of the comments, too. :)

David K on April 2, 2021

Funny that - I really wish there was a delete function on these comments, I realised it was mentioned afterwards :) Thanks for a good overview on Django :)

mochibeepboop on Sept. 10, 2023

What a great course! Quick question, why do we need {"project": project} in views.py? Why append the project as a value of a dictionary key? What accesses this value?

def project_detail(request, pk):
    project = Project.objects.get(pk=pk)
    return render(request, "projects/detail.html", {"project": project})

Thanks!

Bartosz Zaczyński RP Team on Sept. 11, 2023

@mochibeepboop render() is a shortcut function provided by Django, which expects a dictionary as an optional third parameter. It’s called the context of your template, and it’s a standard way of passing named variables into the template, letting you interpolate an expression like this:

<h1>Project name: {{ project.name }}</h1>

Other frameworks, such as Flask, use a similar pattern, but instead of expecting a dictionary, they let you pass a variable number of arguments (*args):

@app.route("/project/details/<pk:int>")
def project_details(pk):
    project = Project.objects.get(pk=pk)
    return render_template("project_details.html", project=project)

Ultimately, the idea is the same in both web frameworks.

Become a Member to join the conversation.