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:
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.