What Is a "Slug" in Django

What is a slug in Django?

A "slug" is a way of generating a valid URL, generally using data already obtained. For instance, a slug uses the title of an article to generate a URL. I advise to generate the slug by means of a function, given the title (or another piece of data), rather than setting it manually.

An example:

<title> The 46 Year Old Virgin </title>
<content> A silly comedy movie </content>
<slug> the-46-year-old-virgin </slug>

Now let's pretend that we have a Django model such as:

class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField(max_length=1000)
slug = models.SlugField(max_length=40)

How would you reference this object with a URL and with a meaningful name? You could for instance use Article.id so the URL would look like this:

www.example.com/article/23

Or, you might want to reference the title like this:

www.example.com/article/The 46 Year Old Virgin

Since spaces aren't valid in URLs, they must be replaced by %20, which results in:

www.example.com/article/The%2046%20Year%20Old%20Virgin

Both attempts are not resulting in very meaningful, easy-to-read URL. This is better:

www.example.com/article/the-46-year-old-virgin

In this example, the-46-year-old-virgin is a slug: it is created from the title by down-casing all letters, and replacing spaces by hyphens -.

Also see the URL of this very web page for another example.

Why do I need to use SlugField in Django?

You don't strictly need to use a SlugField.

"Slug" is a term borrowed from journalism that refers to a short version of a title. As mentioned in a comment to your answer, it's a way to make URLs more explicit while still keeping them somewhat short, instead of using, for example, the full title (which would often be too long), or an ID (which wouldn't be explicit or memorable at all: think of a user who wants to find an article they remember reading: if they start typing some keyword in their address bar, a URL that contains it will pop up, one with an ID will not).

If you wanted you could craft your own slug, by making it URL-friendly (remove any symbol that a URL wouldn't hold, converting anything that would need to be url-encoded, turning spaces into hyphens...) and removing anything unnecessary (e.g. removing words like the, a, an, is, are... or cropping lenghty titles to a maximum number of words or characters).

SlugField is simply a convenience you can use to automate that to some extent. It also comes with some extra features that you might need: for example it automatically generates a slug from a field of your choice, and it can add a unique number to the slug so that you don't accidentally end up with two identical URLs for two different articles that have the same title.

The reason it is a field is that, although you could, it's not smart to calculate a slug every time you access an object: the slug will only change when the title changes, meaning possibly never, so it makes sense to generate it only once and then store it in the database to use it next time, without having to produce it again. This has the added advantage of making a URL to a certain article permanent: you could make it so the slug won't change even if you change the title of the article, which would be a good thing.

Once you have it, since a slug refers unambiguously to a specific object, it acts as a sort of human-readable unique ID, and so it can be used to retrieve the object from the database as efficiently as an opaque numeric ID. It also obscures how many objects you have (if for some reason you want to do that), since a sequential ID of, say, 1543, tells anyone that you probably have 1542 other objects that came before that one.

What is the use of SlugField in django?

We can slugify and store the value in a CharField right? then why do we need SlugField for something that can be done easily using CharField?

A SlugField is a CharField, but a special CharField. It is a field that contains a validator that will, if you for example enter data through a form, validate if the entered string is indeed a slug. For example a slug does not contain whitespace, so if you would use the SlugField in a form, such that an author can specify the slug themselve, it will reject such slugs.

By default it uses the django.core.validators.validate_slug validator [GitHub]. The SlugField has an extra parameter allow_unicode. If this is set to True, it will use the django.core.validators.validate_unicode_slug validator [GitHub].

So in essence it is indeed a CharField, but with a validator that is determined by the **allow_unicode=… parameter [Django-doc]. This thus makes it more convenient.

Often you do not want to slugify the content of the slug field itself, but from another field, for example the title of a blog post entry. You thus still want to preserve the title, but make a slugified variant.

The django-autoslug package [readthedocs] provides an AutoSlugField [readthedocs] which is more convenient, since you can define how to populate the field, and thus it will automatically slugify:

from autoslug import AutoSlugField

class MyModel(models.Model):
title = models.CharField(max_length=128)
slug = AutoSlugField(populate_from='title')

Django: how to create slugs in django?

Creating posts with the same slug makes not much sense, since a slug is used to determine what Article is used. If two items have learning-to-code, then you can not determine which of the two Articles is the right one.

If your Article for example has a DateField, you can use this such that is works with both the date and the slug to determine the Article. The easist way to achieve that is likely with the django-autoslug package [PyPi]. This will work with a UniqueConstraint [Django-doc], such that the combination of the date and the slug is unique, so:

from autoslug import AutoSlugField
from django.conf import settings

class Article(models.Model):
title = models.CharField(max_length=200)
slug = AutoSlugField(populate_from='title', unique_with=['publish_date'])
description = models.TextField(blank=True, null=True)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
null=True
)
publish_date = models.DateField(auto_now_add=True)

The urls.py will need to add specifications for the date, so:

path('<int:year>/<int:month>/<int:date>/<slug:slug>', some_view, name='article_detail')

then in a DetailView, or other views, we can filter on the QuerySet with the date:

from datetime import date
from django.views.generic import DetailView

class ArticleDetailView(DetailView):
model = Article

def get_queryset(self, *args, **kwargs):
return super().get_queryset(*args, **kwargs).filter(
publish_date=date(self.kwargs['year'], self.kwargs['month'], self.kwargs['day'])
)


Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

How do i use slug urls correctly in Django?

A slug never contains a slash. It looks like your url is prefixed with posts/. So you can alter your urls.py with:

from django.contrib import admin
from django.urls import path
from posts.views import posts_list, posts_detail

urlpatterns = [
path('admin/', admin.site.urls),
path('posts/', posts_list, name='posts_list'),
path('posts/<slug:slug>/', posts_detail, name='unique_slug'),
]

It is probably better to add the type of the path converter, so <slug:slug>.

You might want to use django-autoslug [GitHub] to automatically construct a slug based on a certain field.

How can I use slugs in Django url

So you haven't posted your code, but assuming your model looks like this:

class Post(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField(unique=True)
content = models.TextField()

And that you want to pre-fill the slug from the title, you have a few options depending on where you want to do it:

  1. Post will be created only by staff users: pre-populate it in the admin
  2. Post will be created outside of the admin: override .save() method

From the admin

The easiest way to get this in the admin is via the prepopulated_fields

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
prepopulated_fields = {'slug': ('title',)}

Django will auto-update the slug field as you type the title when creating a post. Very nice UX, but limited to the admin...

Outside of the admin

In the previous example, you might end up with an empty slug if you were to create a post from the console, or from another page. In this case, you can quickly make sure slug is prepopulated by overriding the model's .save() method, and calling slugify in it:

class Post(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField(unique=True)
content = models.TextField()

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

Link Post by slug

Disclaimer: if you need more details on this part, I suggest the part 3 of the official tutorial.

Provided you have a URL path:

# urls.py
from django.urls import path
from your_blog import views

urlpatterns = [
path('posts/<slug:the_slug>/', views.post_detail_view, name='show_post'),
]

Then, in your views module you have a view:

# your_blog/views.py 
from django.views.generic.detail import DetailView

class PostDetailView(DetailView):
model = Post
# This file should exist somewhere to render your page
template_name = 'your_blog/show_post.html'
# Should match the value after ':' from url <slug:the_slug>
slug_url_kwarg = 'the_slug'
# Should match the name of the slug field on the model
slug_field = 'slug' # DetailView's default value: optional

post_detail_view = PostDetailView.as_view()

You can link to a Post by calling, in Python:

reverse('show_post', args=[the_post.slug])

Or in a Django template:

<a href="{% url 'show_post' the_post.slug %}">{{ the_post.title }}</a>

EDIT: Post index page

You could then add an index page, generating a list linking to all your posts:

# your_blog/views.py 
from django.views.generic import ListView

class PostListView(ListView):
model = Post
# This file should exist somewhere to render your page
template_name = 'your_blog/list_post.html'

And in the view template:

<!-- your_blog/list_post.html -->
<ul>
{% for the_post in object_list %}
<li>
<a href="{% url 'show_post' the_post.slug %}">{{ the_post.title }}</a>
</li>
{% endfor %}
</ul>

Hope that helps :)

django: how to add slug as arguments in url tag using django

You can use like keyword arguments and its accepted the same way, and its more explicit which argument is which.

Do like this:

<a href="{% url 'base:tutorial-page' prg_slug=p.slug prglangcat_slug=p.slug2 %}"  </a>


Related Topics



Leave a reply



Submit