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 usingCharField
?
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 Article
s 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 theUser
model [Django-doc] directly. For more information you can see the referencing theUser
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:
Post
will be created only by staff users: pre-populate it in the adminPost
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
How to Use a Dll File from Python
Shared-Memory Objects in Multiprocessing
Unicode (Utf-8) Reading and Writing to Files in Python
Sort() and Reverse() Functions Do Not Work
Editing Specific Line in Text File in Python
Saving and Loading Multiple Objects in Pickle File
Python App Does Not Print Anything When Running Detached in Docker
Delete an Element from a Dictionary
Can Not Click on a Element: Elementclickinterceptedexception in Splinter/Selenium
Reading Rather Large JSON Files
What Are "First-Class" Objects
Pandas: Filter Rows of Dataframe with Operator Chaining
Scrape Multiple Urls Using Qwebpage
Changing User Agent on Urllib2.Urlopen
How to Fix Overlapping Annotations/Text
What's the Simplest Way of Detecting Keyboard Input in a Script from the Terminal