Skip to main content

Addendum: Pattern Matching in URLs

In previous sections, we have created the /courses URL and bound it to the courses() view, which displayed a list of courses stored in courses.db. Now suppose that you want to create a course() view where the details of a particular course are displayed. For this, you could configure a /course URL followed by the name of the course from which you want to return the information, for example, /course/Python, /course/Java, /course/PHP, etc. Let's do it so by adding that configuration to our myapp/urls.py file:

urlpatterns = [
path("", views.index, name="index"),
path("about-us", views.about_us, name="about_us"),
path("courses", views.courses, name="courses"),
path("courses/json", views.courses_json, name="courses_json"),
path("course/<str:course_name>", views.course, name="course")
]

What's new here is the <str:course_name> in the URL route, which is called a pattern. This pattern tells Django to “capture” a string (hence the str) located right after course/ and to save it in the course_name variable. Then it will be passed as argument to the specified view, views.course(), which in our myapp/views.py would look like this:

def course(request: HttpRequest, course_name: str) -> HttpResponse:
conn = sqlite3.connect("courses.db")
cursor = conn.cursor()
cursor.execute("SELECT name, enrolled_students FROM courses WHERE name=?",
[course_name])
course = cursor.fetchone()
ctx = {"course": course}
conn.close()
return render(request, "myapp/course.html", ctx)

We are using the course_name argument (in which Django put the name of the course it captured from the URL) to get the information for that particular course from an SQL query (WHERE name = ?). As expected, if the user enters http://127.0.0.1:8000/myapp/course/Python, then the value of course_name is "Python", if he enters http://127.0.0.1:8000/myapp/course/Java, then it is "Java", and so on.

Finally, create the templates/myapp/course.html template:

<h1>Course information for {{ course.0 }}</h1>
Registered: {{ course.1 }}

And the result is:

Course Information

If cursor.fetchone() returns None, it means that the name of the course stored in course_name does not correspond to any course in the database. In that case, you can generate a 404 (Not Found) error by throwing the django.http.Http404 exception:

from django.http import Http404

# ...

def course(request: HttpRequest, course_name: str) -> HttpResponse:
# ...
course = cursor.fetchone()
if course is None:
raise Http404
ctx = {"course": course}
conn.close()
return render(request, "myapp/course.html", ctx)

You might also need to to capture integers from a URL. For example, if each course had a numerical identifier, you could do in myapp/urls.py:

urlpatterns = [
path("", views.index, name="index"),
path("about-from", views.about_from, name="about_from"),
path("courses", views.courses, name="courses"),
path("courses/json", views.courses_json, name="courses_json"),
path("course/<int:course_id>", views.course, name="course")
]

And then in the view:

def course(request: HttpRequest, course_id: int) -> HttpResponse:
# ...

This course/<int:course_id> pattern captures integers in the URL and passes them as the course_id argument to the course() view. For example, /course/5, /course/1234, etc., but not /course/Python, since it is not an integer. When the user visits a URL that does not match any pattern defined in urls.py, Django automatically throws a 404 error.