URL Linking: app_name, Path Names, and Arguments
In this lesson, you’ll learn about linking templates together, namespaces in Django, path names, and passing arguments in URLs. You’ll also make friends with a new error: NoReverseMatch
!
00:00
In this video, we’ll talk about linking templates together, we’ll talk about namespaces in Django, what’s the app_name
, how do you assign path names, and also how to pass arguments around in your URLs.
00:14 So, the a first thing we want to do is we want to try to link two templates. Let’s take a look.
00:21 In our finished project, we have this list view where we can see all of the projects listed in our database. We have this button where we can click to get to the detail view.
00:33 Now in the project so far, we’ve implemented this detail view—we can go here and access it. However, we have this button there, and when we press the button, it currently doesn’t do anything.
00:44
That’s what we want to fix here. We want to go into this all_projects
template and change something inside of the HTML that we have there in order to make this button working.
00:57
I’m here in all_projects.html
, and this is the button that we looked at just before. In here is where we have to put the URL to link forward. Now, this shouldn’t be just a static URL.
01:09
We don’t want to hardcode it, because it’s supposed to be different for each project that we’re putting in there, right? So, what can we put in here? Django has this url
tag.
01:20 It’s a Django template tag that looks like this, and then something follows after here. I’m going to run it, for now, like this. We keep it empty because that’s the piece that we’re going to talk about a lot more. But at first, let’s see what happens when we run it like this.
01:39 I’m clicking the button now.
01:44
I’m going to reload the /projects/
page. And what we get now is a NoReverseMatch
error. It’s telling us that the Reverse for
—and this maybe looks like a double quote, but it’s actually two single quotes. You can see it down here.
01:58
So, we have two single quotes, which is just what I put—if you remember, I put url
and then these two single quotes. That’s just what it’s telling us here, that it couldn’t find a reverse for an empty string, essentially.
02:14 That it’s not a valid view function or a pattern name. We are going to focus on this pattern name. We’re going to figure out how to assign pattern names and how to deal with namespaces in Django in general.
02:25
And yes, NoReverseMatch
is our new friend here, and I’m going to take this error as an example to dive a bit deeper into debugging errors, because it’s an error that comes up a lot and we want to practice some ways of like, “Where can we look?
02:39 How can we dig down and how can we figure out how to solve this error?” The first thing that we want to do is we want to deal with the missing pattern name.
02:48 We want to figure out what that is all about. And for that, we want to give names to patterns. The path that we just recently built, looked like this. Let’s take a look at what are the different names that we can give, that we can assign here to create a proper namespace.
03:03
So, here’s the path that we just recently built, and let’s take a look at how can we correctly link to this path coming from our template? I started off making this url
tag and then left this part here empty.
03:15
Now, what we can see here is it says 'app_name:name'
and then we also have this other part here: the argument that we’re going to talk about later. First, we’re going to make a functioning one just with these two parts.
03:27 We’re going to talk about that because this is the namespace that helps us to directly get to the specific view that we’re pointing to. Our task now is to give a name to the patterns in here so that we’ll be able to address it like this inside of the URL.
03:45
The first thing we need to do, inside of urls.py
we have to define the app_name
variable. That’s just how it’s called. We give it the name of our app—in this case, "projects"
.
03:57
And this is what’s going to come over here inside of the URL string, defining it. So, that’s the name of the app. First, the app_name
, and second, we’re going to need the name of the path, and we can add this as a keyword argument inside of the path()
, which is simply name=
and then we can give it any kind of name that we want to. Generally, I try to keep it the same as the view that this is pointing to. And, as you might guess, we’re going to pop that name
over here. So we have url
, app_name
, :
, and then the name
of the path()
that we’re directing to.
04:35 This uniquely identifies a Django view. That’s the namespace for our Django view, and if we put this into the URL, it’s going to know where to direct us to. Okay, let’s go ahead and fix that in our project and then see what happens next.
04:51
First step, we said, we have to go to urls.py
, and we have to add an app_name
.
05:05 I’m naming this the same as our app, okay? Next, we want to add names to our paths.
05:27
And with these two features, the name
and the app_name
, you can uniquely identify the views that we’re pointing to. So over here, in all_projects.html
, I want to now point to app_name
, 'projects'
, and path()
name
, project_detail
.
05:49
That’s what I named it over here—'project_detail'
and app_name
, 'projects'
.
05:59 Okay, so this should create a proper namespace and uniquely identify the view. Let’s give it a try if we can—oh. We can also see the server started again, so let’s give it a try whether everything is fine and whether we can use the button to get to the details page. All right.
06:19
So, after the reloading this I got, again, a reverse match error. I got another NoReverseMatch
error, but it is a different one. If you look here, it’s telling us not anymore that there is no valid pattern name or view function, but it tells us the reverse for 'project_detail'
with no arguments was not found.
06:38
This is the important part here. It did find a reverse for 'project_detail'
, but not one that doesn’t require arguments. And then there’s some regular expressions here, some patterns that it tried.
06:51 It’s also pointing us to where the problem sits, down here. Always take a quick look at these error messages. They’re very useful. So, what we need to do is we need to pass an argument forward in order to be able to retrieve the right URL.
07:05
We’re looking for a URL that has the /
and then the primary key of the resource that we want to get, and we need to be able to pass that forward.
07:16
The way to do this is that inside of the template, additionally to the namespace consisting of app_name
and the name
of the path, separated by a space, we can put in a number, essentially, here.
07:29 We’re going to want that to be a different number, depending on what’s the resource that we want to access, but essentially it just has to be some kind of argument that gets passed forward to the function. Let’s take a look at this.
07:42
If I would go in here, make a space and then just put a 1
here, for example, so I’m passing the number 1
every time. Let’s take a look at the effect that this has on our app.
07:55
I can reload. There’s no errors coming up. And now when I press, this button—yay! We’re getting to a details view. The details view number 1
, which is correct for this one. What happens when I press this one? All right.
08:08
So, I get redirected again to the number 1
, because simply every time this URL—this button—gets clicked, we’re just passing 1
forward. That’s not exactly what we want, but we’re getting close, right?
08:24
To explain this again, this is the thing. Whatever comes in here is getting passed on to the browser to create the URL, and this is the URL that it’s going to retrieve and pass forward to the view function, which then decides based on that, which Project
to grab from the database and what to render.
08:42
To make this functional in the way that we were thinking about it, I’m going to change this to project.pk
, which—again—is the same as project.id
, in our case.
08:55
And this is just going to be 1
for the first project, 2
for the second project, et cetera. So now, we’re passing in this number—that is relative to the project that we’re looking at—into the URL resolver here, so that then, it’s going to direct us forward to the appropriate details page.
09:18
Clicking on project number 1
takes me to the URL /1
, and clicking on the project number 2
takes me to the URL /2
, which is the details page of our second entry in the database.
09:32
Good job! That’s what we wanted to achieve, and now our functionality here already mirrors the functionality of our finished app! So, remember to thank NoReverseMatch
for that, because it’s really an error you might be encountering very often.
09:47
In the next video, we’re going to take a look at what are the places to look at and how to properly debug when you see a NoReverseMatch
error.
reblark on Nov. 13, 2019
Now, I see it. but it doesn’t look right… BTW, “single” is pronounced “sing ul”
reblark on Nov. 13, 2019
Sorry to bother, but I just cannot make this work and have examined the code to near death: ‘Page not found (404) Request Method: GET Request URL: localhost:8000/projects/4%7D Using the URLconf defined in CA.urls, Django tried these URL patterns, in this order: admin/ projects/ [name=’all_projects’] projects/ <int:pk> [name=’project_detail’] The current path, projects/4}, didn’t match any of these.
This is the error message, not the “NoReverseMatch” message…4 is the id number for the first database entry. If I simply put “4” as the argument, I get the page and then when I click on the button, I get this error message.
reblark on Nov. 13, 2019
Wow. Persistence, persistence, persistence and then try again. Your selling the “error messages are your friends” pitch is terrific. I looked at the error messages many, many times. But, finally I saw something that was giant, “that’s not right.” and, it wasn’t, now my code works. I really appreciate your constant “harping” on this message and I like the images because they make the “harping” palatable. I am very grateful.
Lokman on Jan. 8, 2020
Hi @Martin, for code here:
<a href={% url 'projects:project_details' project.pk %}
Why can’t we just replace app_name since we already defined the variable in ./projects/urls.py
Lokman on Jan. 8, 2020
Hi @Martin, for code here:
<a href={% url 'projects:project_details' project.pk %}
Why can’t we just replace app_name at ‘projects’ since we already defined the variable in ./projects/urls.py
>>> from django.urls import include, path
>>> from projects import views
>>> app_name = 'projects'
>>> urlpatterns = [
path('', views.all_projects, name='all_projects'),
path('<int:pk>', views.projects_detail, name='projects_detail'),
]
Thanks
rolandgarceau on Jan. 16, 2020
The beginning is very confusing to know exactly what linking two templates actually is. Just starting to describe existing functionality dooes not clarify this. If it is the # in the href that signifies template linking, then explain that. If it is the process in which one has to implement a work flow to get to the linking, then explain the process and the steps before just diving into how to change a file. This makes it very difficult to follow, especially for those never exposed to template linking as it is being described with django and for nested project structures.
Martin Breuss RP Team on Jan. 17, 2020
Ah, I just see that that’s a confusion I introduced with calling it “Template linking” in the video.
A better way to name that into slide might be:
Creating URLs in Django Templates
I’m attempting to explain what Django does behind the scene and where it gets the information from that it needs to construct a valid URL that can point users from one page (built by a template) to another page (built by a different template).
Linking With URLs
Instead, I am accessing the resources that are exposed at different URLs, specifically:
http://localhost:8000/projects
andhttp://localhost:8000/projects/1
.
In Django, and many other web frameworks, the layout of the data that is available at a certain URL is defined inside of a template. Templates are the blueprints for the HTML page that ultimately gets displayed by your browser when you access a certain URL.
Just like any webpage, they can contain links to other resources (=other URLs), and in Django in order to display a link on your final HTML page, you can edit the template that is what Django creates the HTML page from.
URL Fragments (#
)
What might look a bit confusing is the #
that you spotted in the URL after I clicked on the button. The resulting URL looks like so:
http://localhost:8000/projects/#
This URL points to the same resource as /projects
without the #
. This part of a URL is called a “fragment” or “anchor” and points to a specific section in the same resource.
MDN explains on their page about URLs:
#SomewhereInTheDocument
is an anchor to another part of the resource itself. An anchor represents a sort of “bookmark” inside the resource, giving the browser the directions to show the content located at that “bookmarked” spot. On an HTML document, for example, the browser will scroll to the point where the anchor is defined; on a video or audio document, the browser will try to go to the time the anchor represents. It is worth noting that the part after the #, also known as the fragment identifier, is never sent to the server with the request.
The code you’re working with contains a default #
in the href
attribute of the button link element you’re creating:
<a href="#" class="btn btn-primary">Read more </a>
This just means “stay on the current page when clicked”. If you use the fragment in a link’s href
without any further identifier, the #
takes you back to the top of the current resource, which can be used for “Scroll to the top” links.
In this case you don’t see a change since you’re already at the top of the page.
Editing The Template
In order to make the link useful, you then edit the href
attribute in the template. The edits you make in here ultimately create a URL that gets pointed to when you click the link, and this video explains what happens and how the final URL gets created in Django.
Hope that helps and clarifies the situation.
Martin Breuss RP Team on Jan. 17, 2020
hei @Lokman. If I understand correctly, you’re wondering why you can’t write:
<a href={% url 'app_name:project_details' project.pk %}
in your template instead of the following (which is correct):
<a href={% url 'projects:project_details' project.pk %}
? (Please clarify in case I misunderstood)
The reason that you can use the variable name projects
inside of your link is because you defined it in /projects/urls.py
. Without setting that app_name
variable, you would not be able to use it for linking.
And you want to be able to use the value of the app_name
variable in the templates explicitly, since it allows you to link to different apps with separate url configurations.
E.g. if you had another Django app in your project, let’s call it blog
, that contains its own file for url configurations /blog/urls.py
and you also set the app_name
variable in there, then you could theoretically link to a resource even by using the same name
, like so:
from django.urls import path
from blog import views
app_name = 'blog'
urlpatterns = [
path('', views.project_details, name='project_details'),
]
<a href={% url 'blog:project_details' %}
DISCLAIMER: This does not make a whole lot of sense :) and it’s always better to give separate, distinctive, and descriptive names to all of your functions.
I’ve used this example only to exemplify that if we’d use app_name
instead, then Django wouldn’t know which app it should go to look for a resource in. Hope this helps.
Tl;dr
Setting app_name
allows you to use its value to create relative URLs in your templates that know which app of your project to direct to.
vikramjeetsra on March 31, 2020
Hi
I am getting this error NoReverseMatch at /blog/ Reverse for ‘post’ with arguments ‘(‘’,)’ not found. 1 pattern(s) tried: [‘blog/(?P<pk>[0-9]+)$’]
even though i added path (“<int:pk>”, views.post, name=”post”), in url
and <a href=”{% url ‘blog:post’ post.pk %}” class=”btn btn-primary”>Read More</a> in html
def post(request, pk): post = Post.objects.get(pk=pk) return render(request, ‘blog/post.html’, {‘post’: post})
in the view
Martin Breuss RP Team on March 31, 2020
Hei @vikramjeetsra! Looks like a lot of your code set up seems fine to me. When you check your initial error, there’s a hint to what might be going wront:
NoReverseMatch at /blog/ Reverse for ‘post’ with arguments ‘(‘’,)’ not found. 1 pattern(s) tried: [‘blog/(?P<pk>[0-9]+)$’]
Specifically, look at this part: with arguments ‘(‘’,)
. Seems like there was nothing to find after /blog/
in your URL. Are you sure you entered a number after the slash, like so:
http://localhost:8000/blog/1
okorobright13 on April 15, 2020
hi everyone!!!, please can anyone help me to fix this,i can not see the blue button Read More of this code?.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
</head>
<body>
{% extends "base.html" %}
{% load static %}
{% block page_content %}
<h1>Projects</h1>
<div class="row">
{% for project in projects %}
<div class="col-md-4">
<div class="card mb-2">
<img class="card-img-top" src="{% static project.image %}">
<div class="card-body">
<h5 class="card-title">{{ project.title }}</h5>
<p class="card-text">{{ project.description }}</p>
<a href="{% url 'projects:project_detail' project.pk %}"
class="btn btn-primary">
Read More
</a>
</div>
</div>
</div>
{% endfor %}
</div>
{% endblock %}
</body>
</html>
Martin Breuss RP Team on April 17, 2020
Hello @okorobright13, the HTML that makes this button is the following Link Element:
<a href="{% url 'projects:project_detail' project.pk %}" class="btn btn-primary">
Read More
</a>
I am not sure why it would not display for you. Could you share more of your project code, maybe via GitHub? Also check out Bootstrap’s Docs on Buttons, that might help you forward.
Martin Breuss RP Team on April 17, 2020
It is also possible that you are not passing any projects
to your template. Double-check your views.py
file whether you are fetching the database entries from the database, as well as passing them on to the template correctly.
Another thing to check is whether you have any entries in the database. If there’s nothing in there, then your template won’t have any posts to display.
Joel Witherspoon on May 2, 2020
Thanks for explaining the namespace for views. I was confused as to their purpose when I used another resource to learn Python.
Muthukumar Kusalavan on May 3, 2020
thanks for your work, Mr.martin. i have one question.
so everytime, i do pk, i need to update like this? even if it is 100?
p2 = Project.objects.get(pk=2) p1 = Project.objects.get(pk=1) ........ pn2 = Project.objects.get(pk=n)
Bill Lafferty on June 11, 2020
Hi all,
I get the NoReverseMatch error on the main project page (named ‘farmapp’ in my case). The individual projects under farmapp do display correctly, though, if I append the url with /1, /2, etc.
Looks like I have the appname and namespace set up correctly. I’m stumped. Any ideas where else I can look?
urls.py
app_name = "farmapp"
urlpatterns = [
path('', views.all, name='all'),
path('<int:pk>', views.produce_detail, name='produce_detail'),
]
template –> all.html
<a href="{% url 'produce_detail' project.pk %}" class="btn btn-primary">
Thanks. Bill
Bill Lafferty on June 11, 2020
Hi again,
Forgot to add the error message I’m getting!
NoReverseMatch at /farmapp/
Reverse for ‘produce_detail’ not found. ‘produce_detail’ is not a valid view function or pattern name.
Request Method: GET
Request URL: localhost:8000/farmapp/
Django Version: 3.0.6
Exception Type: NoReverseMatch
Exception Value:
Reverse for ‘produce_detail’ not found. ‘produce_detail’ is not a valid view function or pattern name.
Exception Location: C:\Python\portfolio.env1\lib\site-packages\django\urls\resolvers.py in _reverse_with_prefix, line 677 Python Executable: C:\Python\portfolio.env1\Scripts\python.exe Python Version: 3.8.3 Python Path:
[‘C:\Python\portfolio’, ‘C:\Python\Python38\python38.zip’, ‘C:\Python\Python38\DLLs’, ‘C:\Python\Python38\lib’, ‘C:\Python\Python38’, ‘C:\Python\portfolio\.env1’, ‘C:\Python\portfolio\.env1\lib\site-packages’]
Martin Breuss RP Team on June 11, 2020
Hi @Bill!
Since you’re using both the app_name
and the name
argument in your path()
, you also need to refer to both in your {% url %}
tag. Currently you are only mentioning the name
argument here:
<a href="{% url 'produce_detail' project.pk %}" class="btn btn-primary">
How would you fix it up and correctly add the app_name
also into your link here?
Martin Breuss RP Team on June 11, 2020
Hi @Muthukumar Kusalavan, sorry I missed your question. I am not sure I completely understand, but maybe this helps:
Inside of your view function in views.py
, your pk
is going to be a variable that changes depending on what URL the user tries to access. So it will automatically change to the number of the project that you are trying to access.
But yes, every time you want to get a specific entry from the database, you need to pass the specific pk
of that object (in this case - and usually - an integer) to the database call. Hope that helps!
Bill Lafferty on June 11, 2020
Hi Martin,
Ok, to add the app_name
, I think I need to do as follows. Still not working, though. Am I on the right track?
<a href="{% url 'farmapp.views.produce_detail' project.pk %}" class="btn btn-primary">
Martin Breuss RP Team on June 11, 2020
Totally on the right track, only the syntax for this is a little different. Instead of a .
to separate app_name
and name
, you need to use a :
. :)
Martin Breuss RP Team on June 11, 2020
That rendered potentially a bit confusing with the colon-dot-smiley, so here’s the correct syntax:
'app_name:name'
Martin Breuss RP Team on June 11, 2020
Also keep in mind that this is not a reference to views.py
, as you were trying to establish, it seems.
You are exclusively building these URLs through the information inside of urls.py
, which is why all you need to construct the URL properly is the 'app_name:name'
- both of which are located in urls.py
.
Hope that helps to clarify this!
Bill Lafferty on June 11, 2020
Makes sense, thank you! Noted that it’s not in reference to views.
However, it is still giving me trouble. This gives me the same noreversematch error at /farmapp
error.
<a href="{% url 'farmapp:produce_detail' project.pk %}" class="btn btn-primary">
Martin Breuss RP Team on June 12, 2020
This link should be fine now, you fixed it up correctly. There might be another link in that template that doesn’t have the correct namespace set up? If there is any link on your page that Django can’t resolve, it will give you the NoReverseMatch
error.
Another thing I notice: you mention your template sits in template/all.html
instead of templates/all.html
(note the “s”). Django only automatically discovers folders in apps that are named “templates
”. Might be a typo, but thought I’d mention it.
If none of this solves it you can post a link to your GitHub repo and I can take a look.
Sergey on Sept. 21, 2021
Hello!
I have a question about this url’s linking…
`<a href="{% url 'projects:project_detail' project.pk %}" `
Ok, this is right way to link another url. But… First thing that i did, after defining a function with a ‘pk’:
<a href="{{ project.pk }}"
And this solution also works fine. I think there is a good reason for more complex code. Maybe somebody can explain?
Martin Breuss RP Team on Sept. 23, 2021
Hi @Sergey, great question and kudos for playing around with the code 🙌 :)
One thing to keep in mind is that Django always resolves any code you write into HTML. So what you’re writing into the href
attribute will boil down to strings that this HTML attribute can interpret.
Let’s look at what the two different Django code snippets you posted produce after being rendered (Note: you can do that by inspecting your page source):
href="{% url 'projects:project_detail' project.pk %}"
will resolve tohref="/projects/1/"
for the project withpk=1
.href="{{ project.pk }}"
will resolve tohref="1"
for the project withpk=1
.
Like you said, in the context of this project both will work and bring you to the detail page of that project. Both are relative paths, however, there is a difference:
- /projects/1/ will look at the root of your project and go from there
- 1 will look from where the current template is located
What do you think is the advantage of using one over the other? What happens if you try to use the shorter link to link from another Django app, e.g. the blog
app?
Sergey on Sept. 23, 2021
Hello, @Martin!
Thank you for detailed answer, now i even more appreciate this course and Real Python.
Note: you can do that by inspecting your page source
This is a really great suggestion! And such simple!
What happens if you try to use the shorter link to link from another Django app, e.g. the blog app?
I think in best case scenario this will link button to some 404 Not Found…
Many thanks for you work, RP Team! Great course, great resource.
Calvin on Jan. 17, 2023
If anyone was following along and didn’t catch why you’re still getting NoReverseMatch/namespace issues, you will need to register the app_name
in your urls.py
file.
e.g.
from django.urls import path # remove include in this app...
from projects import views
app_name = 'projects'
# add app routes here
urlpatterns = [
path('', views.all_projects),
path('<int:pk>', views.project_detail, name="project_detail"),
]
Make sure to test and verify.
Become a Member to join the conversation.
reblark on Nov. 13, 2019
At 1:05, your html code for all_projects.html is missing: <div class=”row”> and of course, the closing tag for div