Creating a detail page
This episode covers :-
1) How to add a detail page.
2) How to create slugs.
3) How to return canonical URLs with get_absolute_url().
4) How to reverse URLS.
5) How to use the {% url %} template tag.
- Setup :-
Terminal
cp -fr 15-Base-Project 16-Detail-Page
cd 16-Detail-Page
source ../venv/bin/activate
- Adding a detail page path :-
Edit mysite app urls.py file and add a path to the detail page:
mysite/urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('flower/<int:id>/', myapp_views.detail, name='deta\
il'), # < here
path('', myapp_views.index, name='index'),
]
- Creating the detail view :-
Edit myapp views.py file and add the detail function:
myapp/views.py
from django.shortcuts import render, get_object_or_404 # < \
here
from .models import Flower
def index(request):
flowers = Flower.objects.all()
return render(request, 'myapp/index.html', {'flowers': \
flowers})
def detail(request, id=None): # < here
flower = get_object_or_404(Flower, id=id)
return render(request, 'myapp/detail.html', {'flower': \
flower})
Make sure to import get_object_or_404.
- Creating the detail page template :-
Create detail.html file in the myapp templates folder:
Detail page template
├── 16-Detail-Page
│ ├── myapp
│ │ ├── templates
│ │ │ └── myapp
│ │ │ ├── detail.html # < here
Fill it with these lines:
myapp/templates/myapp/detail.py
{% extends 'base/base.html' %}
{% block content %}
<div class="jumbotron">
<div class="container">
<h1 class="display-3">{{ flower.title }}</h1>
<div class="lead">{{ flower.description }}</div>
</div>
</div>
<a href="">Back<a>
{% endblock %}
Visit http://127.0.0.1:8000flower1/ and you should see the detail page jumbotron:
- Creating slugs :-
Accessing individual flowers with an id is not the most friendly approach. Let’s add a SlugField to hold a human-readable path.
Edit myapp models.py file and add a SlugField:
myapp/models.py
from django.utils.text import slugify # < here
from django.db import models
class Flower(models.Model):
title = models.CharField(max_length=255, default='')
description = models.TextField(default='')
slug = models.SlugField(blank=True, default='') # < here
def __str__(self):
return self.title
def save(self, *args, **kwargs): # < here
self.slug = slugify(self.title)
super(Flower, self).save()
We create the slug using the slugify() function in the save method.
Edit the detail function in the myapp views.py file and change all id occurrences to slug:
myapp/views.py
def detail(request, slug=None): # < here
flower = get_object_or_404(Flower, slug=slug) # < here
return render(request, 'myapp/detail.html', {'flower': \
flower})
- Updating the path :-
Edit mysite app urls.py file and change the detail path:
mysite/urls.py
#path('flower/<int:id>/', myapp_views.detail, name='detail'\
),
path('flower/<slug:slug>/', myapp_views.detail, name='detai\
l'),
Run migrations:
Terminal
python manage.py makemigrations
python manage.py migrate
Edit all flowers you have created and save them once to generate slugs.
- Defining get_absolute_url() method :-
We can add a “View on site” link to the admin by defining a get_absolute_url method. Edit myapp models.py file and add the method to the Flower class:
myapp/models.py
from django.utils.text import slugify
from django.db import models
from django.urls import reverse # < here
class Flower(models.Model):
...
def __str__(self):
...
def save(self, *args, **kwargs):
...
def get_absolute_url(self): # < here
return reverse('detail', args=[str(self.slug)])
Edit a Flower object and you will see a link on the top right corner. Click it to visit the flower detail page:
- Using url tag :-
Edit myapp index.html file and use the url tag to link the card to the detail page:
myapp/templates/myapp/index.html
<h5 class="card-title"><a href="{% url 'detail' flower.slug\
%}">{{ flower.title }}</a></h5>
Note: make sure that each flower has a slug by editing and saving them once.
Visit the frontpage and click a title to see the detail page.
- In Details :-
Capturing URL values :-
You can use angle brackets to capture values from the URL. In here we first captured the id number and then the slug:
mysite/urls.py
#path('flower/<int:id>/', myapp_views.detail, name='detail'\
),
path('flower/<slug:slug>/', myapp_views.detail, name='detai\
l'),
You can optionally specify a converter type. int converter type in <int:id>means that the path matches only integers.
Using view parameters :-
In the myapp views.py file we specify a slug parameter. The slug from the URL will be stored in this variable. slug=None means that the default value is None if a parameter is not passed to this view.
myapp/views.py
def detail(request, slug=None):
get_object_or_404 returns “404 Page not Found” error if the object doesn’t exist. Otherwise the object with the slug from the URL parameter will be stored in the flower object:
myapp/views.py
flower = get_object_or_404(Flower, slug=slug)
Explaining slugs :-
Slug is a short label that contains only letters, numbers, underscores or hyphens. It’s often used to offer user-friendly URLS. “productmacbook/” is better than “product-113zxc/”. In our app we use the title field to create the slug.
In the myapp models.py we add the SlugField and specify blank=True so that the field can be empty for the save() method to run:
myapp/models.py
slug = models.SlugField(blank=True, default='')
Slugify function converts strings to URL slugs. You can find it indjango.utils.text:
myapp/models.py
from django.utils.text import slugify
You can override predefined model methods like save():
myapp/models.py
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(Flower, self).save()
In the save() method we can make something happen when the object is saved. In this case we use it to generate a slug.
We have to call the superclass method super() so that the save method default behaviour will be executed and the object stored in the database.
*args and **kwargs allow you to collect arguments or keyword arguments and pass them to the function. This is a Python concept we don’t explore in this book.
- Reversing URLS :-
You can define get_absolute_url method to calculate a canonical URL for an object. In here we use the reverse() function to get the URL to a flower object:
myapp/models.py
def get_absolute_url(self):
return reverse('detail', args=[str(self.slug)])
The reverse function is similar to the url tag that we used with the card markup. In here we pass the detail path name “detail” and the slug as a parameter to it.
If you have a path like this…
mysite/urls.py
path('flower', myapp_views.detail, name=detail),
… then reverse('detail') will generate flower.
If you have a path like this…
mysit/urls.py
path('flower/<slug:slug>/', myapp_views.detail, name='detai\
l'),
… then reverse('detail', args=[str(self.slug)]) will generate a path like
this floweramelanchier-asiatica/.
Summary
- Use angle brackets with paths to capture URL values: 'flower/<slug:slug>/'.
- get_object_or_404() tries to fetch an object but returns a “Page not Found” error if the object is not found.
- SlugField can be used to store a user-friendly path.
- It’s useful to define the get_absolute_url() method for a model to have an easy access to canonical URLS.
- Use {% url %} tag or {{ object.get_absolute_url }} in templates instead of hardcoding URLS.



No comments:
Post a Comment
If you have any doubts. Please let me know.