How to Create Django SEO Friendly URL using Slugs/SlugField

How to Create Django SEO Friendly URL using Slugs/SlugField

Search engine optimization (SEO) is a very important aspect of any web application especially if you are hoping to get a handful of traffic from search engines such as Google. There are many components/parts involved in building a search engine optimized website and of them is search engine friendly URLs also known as keyword-rich URLS / user-friendly URLs / slug URLs.

Luckily for us, Django comes inbuilt with a SlugField Model Field that allows us to easily generate search engine friendly strings that can be used in our URL patterns for easy creation of pretty URLs or search engine-friendly URLs.

A slug is the part of a URL which identifies a particular page on a website in an easy to read form. A slug usually contains only letters, numbers, underscores or hyphens. They're generally used in URLs."

Take a look at these two URLs below for an article title: "How to Code"

You would agree with me that the second URL looks pretty and it conveys a meaning which is related to the content of our article. In this article, I will be showing you how to SEO friendly URL patterns in Django that looks like the second URL above.

I will be assuming you are familiar with Django installation and setup, so I won't be talking about how to do that.

To get started, create a virtual environment, install Django, create a Django project and create a Django app called "blog" or whatever you want.

For my own example, I will be creating a Django App called "blog" which I will use to create a simple blog App which has an SEO-friendly URL.

Example App

My Blog App will consist of an Article Model, a view and a URL Pattern.

In blog/models.py

class BlogPost(models.Model):
    title = models.CharField(max_length=255)
    pub_date = models.DateTimeField()
    body = models.TextField()
    image = models.ImageField(null=True, blank=True)

In blog/views.py

from django.views.generic import ListView, DetailView
from .models import BlogPost

class BlogListView(ListView):
    model = BlogPost
    context_object_name = 'blog_list'
    template_name = 'blog/blog.html'

class BlogDetailView(DetailView):
    model = BlogPost
    context_object_name = 'blog'
    template_name = 'blog/blog_post.html'

In blog/urls.py

from django.urls import path
from . import views
urlpatterns = [
    path('', views.BlogListView.as_view(), name='blog_home'),
    path('article/<int:pk>/', views.BlogDetailView.as_view(),       name='blog_post'),
]

Follow the steps below to create your pretty URLs.

Use SlugField in the Model

There is a special model Field type in Django for slugs: SlugField.

Create a field named slug with type: SlugField.

In blog/models.py

from django.db import models
from django.urls import reverse

class BlogPost(models.Model):
    title = models.CharField(max_length=255)
    pub_date = models.DateTimeField()
    body = models.TextField()
    image = models.ImageField(null=True, blank=True)
    slug = models.SlugField(default='', editable=False, max_length=200, null = False)
    def get_absolute_url(self):
        kwargs = {
            'pk': self.id,
            'slug': self.slug
        }
    return reverse('post-detail', kwargs=kwargs)

    def save(self, *args, **kwargs):
        value = self.title
        self.slug = slugify(value, allow_unicode=True)
        super().save(*args, **kwargs)

Before saving the instance of a blog article, we convert the title to a slug with the slugify Django command, which basically replaces spaces by hyphens and also removes symbols if present.

As SlugField inherits from CharField, it comes with the attribute max_length which handles the maximum length of the string it contains at the database level.

If we use a SlugField without specifying its max_length attribute, it gets the value of 50 by default, which can lead to problems when we generate the string from a bigger max_length field.

So the trick is to make the title and the slug use the same max_length or make the slug field use a lesser max_lenght.

In blog/urls.py

path('article/<str:slug>-<int:pk>/', ArticleDetailView.as_view() , name='article-detail')

In blog/views.py

from django.views.generic import ListView, DetailView
from .models import BlogPost

class BlogListView(ListView):
    model = BlogPost
    context_object_name = 'blog_list'
    template_name = 'blog/blog.html'


class BlogDetailView(DetailView):
    model = BlogPost
    context_object_name = 'blog'
    template_name = 'blog/blog_post.html'

OR

You can use a function-based view and pass in the post slug and post PK into the view.

def blogPost(request, slug, pk):
    blog = BlogPost.get_post(id=pk)
    context = {
        "blog": blog,
    }
    return render(request, 'article_details.html', context)

In the above example, I am fetching the blog post using the "pk" which is a unique number attached to each data added into your database, fetching data via the "pk" is more reliable and I love using this approach.

In this case, the slug is just being used in the URL and nowhere else, the slug has nothing to do with fetching the article from our database.

So, that's it.

If you follow the above steps, you should have a pretty URL for your articles right now. Before you go, don't forget to share this article on your social media profiles, someone might find it useful.