Django dynamic URLs and templates tutorial

Django dynamic URLs and templates tutorial In Part 1, I got a plain “Hello, world” message showing up on my homepage, and honestly, that felt like a big enough win at the time. But real websites don’t just show static text — they show HTML pages, and they show different content depending on which specific item you’re looking at, like /post/3/ versus /post/7/. That’s what we’re building today.

If you haven’t gone through Part 1 yet, I’d start there first — this post assumes you already have a working home view and a connected urls.py.

From Plain Text to Real HTML (Django dynamic URLs and templates tutorial)

So far, our views have returned plain text using HttpResponse. That works for testing, but it’s not how real pages are built. Instead, Django uses render() to combine a view with an HTML template.

First, create a folder structure inside your app:

pages/
    templates/
        pages/
            home.html

Yes, the nested pages folder inside templates looks repetitive, but it prevents naming conflicts once you have multiple apps with templates of the same name.

Inside home.html, add some basic HTML:

html

<!DOCTYPE html>
<html>
<head>
    <title>My First Django Page</title>
</head>
<body>
    <h1>Welcome to my site!</h1>
    <p>This page is rendered from a real HTML template.</p>
</body>
</html>

Now update your view in views.py:

python

from django.shortcuts import render

def home(request):
    return render(request, 'pages/home.html')

Refresh your browser, and instead of plain text, you’ll see a properly styled HTML page. This one change opens the door to everything — CSS, images, dynamic content, all of it.

Passing Data Into a Template

A template becomes genuinely useful once it can display data from your view. Update your view like this:

python

def home(request):
    context = {
        'name': 'Nima Academy',
        'post_count': 12,
    }
    return render(request, 'pages/home.html', context)

Then inside home.html, use Django’s template syntax to display those values:

html

<h1>Welcome to {{ name }}!</h1>
<p>We currently have {{ post_count }} posts.</p>

The double curly braces are how Django templates pull in Python values. I remember this being the moment templates finally felt powerful to me — the same HTML file can show completely different content depending on what the view sends it.

Now, Dynamic URLs

Here’s the part I was most excited to learn. Instead of a fixed page like /about/, you can build a URL that changes based on what’s requested, like /post/1/, /post/2/, and so on, all handled by one single view.

Update your urls.py to accept a number:

python

from django.urls import path
from . import views

urlpatterns = [
    path('', views.home, name='home'),
    path('post/<int:post_id>/', views.post_detail, name='post_detail'),
]

That <int:post_id> part tells Django to capture whatever number appears in that position of the URL and pass it into the view as an argument.

Writing the View That Handles It

Now in views.py, write a view that accepts that captured value:

python

def post_detail(request, post_id):
    return HttpResponse(f"You're viewing post number {post_id}.")

Visit /post/5/ in your browser, and you’ll see “You’re viewing post number 5.” Change the number in the URL, and the page updates automatically. One view, unlimited pages — that’s the part that made dynamic URLs click for me.

Connecting It to Real Data

This becomes genuinely useful once you connect it to your models, something I covered in my earlier post on the Django app structure. Here’s what a more realistic version looks like:

python

from django.shortcuts import render, get_object_or_404
from .models import Post

def post_detail(request, post_id):
    post = get_object_or_404(Post, id=post_id)
    return render(request, 'pages/post_detail.html', {'post': post})

get_object_or_404 fetches the matching database record, or automatically shows a proper “not found” page if it doesn’t exist. That single function saved me from writing a lot of repetitive error handling myself.

Putting the Full Flow Together

Here’s the complete chain now: a visitor requests /post/5/, urls.py captures 5 as post_id, the view fetches that specific post from the database, and render() combines it with an HTML template to produce the final page. This is genuinely how most content-driven pages on real Django sites work — blogs, product pages, user profiles, all following this exact pattern.

A Small Habit Worth Building Now

As your templates grow, avoid repeating the same HTML — like your header and footer — on every page. Django supports template inheritance using a base template, which I’ll cover properly in a future post. For now, just know it exists so your home.html and post_detail.html files don’t end up with duplicated code.

Common Mistakes at This Stage

  • Forgetting the templates/appname/ nested folder structure, which causes a “template not found” error
  • Mismatching the variable name between the view’s context dictionary and the template
  • Using <int:post_id> in urls.py but naming the function parameter something different in views.py
  • Passing a value into the template but forgetting the double curly braces to display it

Where to Go From Here

If this felt like a lot at once, that’s normal — dynamic URLs and templates were the point where Django started feeling like real web development to me, rather than just following steps. For deeper reference, the official Django documentation on templates covers everything beyond what I touched on here.

Final Thoughts

You went from a single static page to a system that can display unlimited pieces of content through one view, driven entirely by the URL. That’s a genuinely big jump from Part 1. Try adding a second dynamic URL on your own — maybe /category/<str:name>/ — before moving on. It’s the fastest way to make this pattern stick.

Django Views URLs Part 2 – Dynamic URLs සහ Templates සිංහලෙන්

Part 1 එකේදී මම “Hello, world” message එකක් plain text විදිහට homepage එකේ පේන්න හැදුවා, ඇත්තටම ඒ වෙලාවේ ඒක big win එකක් වගේ feel වුණා. ඒත් real websites plain text විතරක් පෙන්නන්නේ නෑ – ඒවා HTML pages පෙන්නනවා, ඔයා බලන specific item එක අනුව different content පෙන්නනවා, /post/3/ සහ /post/7/ වගේ. අද අපි හදන්නේ ඒකයි.

ඔයා Part 1 කියවලා නැත්නම්, මුලින්ම ඒකෙන් start කරන්න – මේ post එකෙන් assume කරන්නේ ඔයාට already working home view එකක් සහ connect කරපු urls.py එකක් තියෙනවා කියලා.

Plain Text එකෙන් Real HTML එකට

මේ වෙනකම් අපේ views HttpResponse use කරලා plain text return කළා. Testing එකට ඒක වැඩ කරනවා, ඒත් real pages හදන්නේ ඒ විදිහට නෙවෙයි. ඒ වෙනුවට, Django render() use කරලා view එකක් HTML template එකක් එක්ක combine කරනවා.

මුලින්ම, ඔයාගේ app එක ඇතුළේ folder structure එකක් create කරන්න:

pages/
    templates/
        pages/
            home.html

ඔව්, templates ඇතුළේ nested pages folder එක repetitive වගේ පේනවා, ඒත් ඔයාට same name එකේ templates තියෙන apps ගොඩක් ආවම, ඒක naming conflicts වළක්වනවා.

home.html ඇතුළේ, basic HTML ටිකක් add කරන්න:

html

<!DOCTYPE html>
<html>
<head>
    <title>My First Django Page</title>
</head>
<body>
    <h1>Welcome to my site!</h1>
    <p>This page is rendered from a real HTML template.</p>
</body>
</html>

දැන් views.py වල ඔයාගේ view එක update කරන්න:

python

from django.shortcuts import render

def home(request):
    return render(request, 'pages/home.html')

Browser එක refresh කරන්න, plain text වෙනුවට, දැන් ඔයාට properly styled HTML page එකක් පේනවා. මේ එක change එකෙන් CSS, images, dynamic content ඔක්කොටම door එක open වෙනවා.

Template එකකට Data Pass කිරීම

View එකෙන් එවපු data එකක් display කරන්න පුළුවන් වුණාම, template එකක් ඇත්තටම useful වෙනවා. ඔයාගේ view එක මෙහෙම update කරන්න:

python

def home(request):
    context = {
        'name': 'Nima Academy',
        'post_count': 12,
    }
    return render(request, 'pages/home.html', context)

ඊට පස්සේ home.html ඇතුළේ, Django ගේ template syntax use කරලා ඒ values පෙන්නන්න:

html

<h1>Welcome to {{ name }}!</h1>
<p>We currently have {{ post_count }} posts.</p>

Double curly braces ටික තමයි Django templates Python values ගේනවා. මට මතකයි templates ඇත්තටම powerful feel වුණේ මේ moment එකේදී – same HTML file එකෙන් view එක එවන දේ අනුව සම්පූර්ණයෙන්ම වෙනස් content පෙන්නන්න පුළුවන්.

දැන් Dynamic URLs

මම ඉගෙන ගන්න වැඩිම excite වුණ කොටස මේකයි. /about/ වගේ fixed page එකක් වෙනුවට, request කරන දේ අනුව වෙනස් වෙන URL එකක් හදන්න පුළුවන්, /post/1/, /post/2/ වගේ, ඔක්කොම handle කරන්නේ එකම view එකකින්.

urls.py එක number එකක් accept කරන්න update කරන්න:

python

from django.urls import path
from . import views

urlpatterns = [
    path('', views.home, name='home'),
    path('post/<int:post_id>/', views.post_detail, name='post_detail'),
]

<int:post_id> කොටසෙන් Django ට කියනවා, URL එකේ ඒ position එකේ එන number එක capture කරලා, view එකට argument එකක් විදිහට pass කරන්න.

ඒක Handle කරන View එක ලියන එක

දැන් views.py වල, ඒ capture කරපු value එක accept කරන view එකක් ලියන්න:

python

def post_detail(request, post_id):
    return HttpResponse(f"You're viewing post number {post_id}.")

Browser එකේ /post/5/ එකට visit කරන්න, “You’re viewing post number 5.” කියලා පේනවා. URL එකේ number එක change කරන්න, page එක automatically update වෙනවා. View එකක්, unlimited pages – dynamic URLs මට click වුණ කොටස ඒකයි.

Real Data එකට Connect කිරීම

Django app structure ගැන මගේ කලින් post එකේ discuss කරපු models වලට connect කරාට පස්සේ, මේක ඇත්තටම useful වෙනවා. More realistic version එකක් මෙහෙම පේනවා:

python

from django.shortcuts import render, get_object_or_404
from .models import Post

def post_detail(request, post_id):
    post = get_object_or_404(Post, id=post_id)
    return render(request, 'pages/post_detail.html', {'post': post})

get_object_or_404 matching database record එක fetch කරනවා, නැත්නම් ඒක exist නැත්නම් automatically proper “not found” page එකක් පෙන්නනවා. ඒ එක function එකෙන් මට repetitive error handling ගොඩක් ලියන්නම ඕන වුණේ නෑ.

Full Flow එක එකට එකතු කිරීම

දැන් complete chain එක මෙහෙමයි: visitor කෙනෙක් /post/5/ request කරනවා, urls.py එකෙන් 5 post_id විදිහට capture කරනවා, view එකෙන් database එකෙන් ඒ specific post එක fetch කරනවා, render() එකෙන් ඒක HTML template එකක් එක්ක combine කරලා final page එක හදනවා. Real Django sites වල content-driven pages ගොඩක් වැඩ කරන්නේ මේ exact pattern එකෙන්මයි – blogs, product pages, user profiles, ඔක්කොම.

දැන් හදාගන්න වටින Small Habit එකක්

ඔයාගේ templates growing වෙනකොට, same HTML – header සහ footer වගේ – හැම page එකකම repeat කරන එක avoid කරන්න. Django base template එකක් use කරලා template inheritance support කරනවා, ඒක future post එකකදී properly cover කරන්නම්. දැනට, ඒක exist වෙනවා කියලා දැනගන්න, ඔයාගේ home.html සහ post_detail.html files වල duplicate code එන්නෙ නැති වෙන්න.

මේ Stage එකේදී Common Mistakes

  • templates/appname/ nested folder structure එක අමතක වීම, ඒකෙන් “template not found” error එකක් ඒම
  • View එකේ context dictionary එකේ variable name එක සහ template එකේ variable name එක mismatch වීම
  • urls.py වල <int:post_id> use කරලා, views.py වල function parameter එකට වෙනස් name එකක් දීම
  • Template එකට value එකක් pass කරලා, ඒක display කරන්න double curly braces අමතක වීම

මෙතනින් ඉදිරියට

මේක එකපාරටම ගොඩක් වගේ feel වුණා නම්, ඒක normal – dynamic URLs සහ templates තමයි Django, steps follow කිරීමක් නෙවෙයි, real web development එකක් වගේ feel වෙන්න පටන් ගත්ත point එක. Deep reference එකක් ඕන නම්, official Django documentation on templates එකේ මෙතන touch කරපු දේට වඩා ගොඩක් තියෙනවා.

අවසාන

ඔයා single static page එකකින්, URL එකෙන්ම drive වෙන, එක view එකකින්ම unlimited content pieces display කරන system එකකට ගියා. Part 1 එකෙන් මේක ඇත්තටම ලොකු jump එකක්. ඊළඟට යන්න කලින්, ඔයාගේම second dynamic URL එකක් try කරන්න – /category/<str:name>/ වගේ එකක්. මේ pattern එක stick කරගන්න තියෙන fastest way එක ඒකයි.

Full Course

Django Views and URLs Part 2: Dynamic URLs and Templates Made Simple

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top
Nima Academy
Privacy Overview

This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.