Search code examples
djangoslug

How can I use slugs in Django url


I have tried searching for this question, but mostly I saw regex patterns, so:

  1. How can I fill my blog model slug field automatically with the post name without filling it out myself

  2. How can I make a link to go to a single post page using the slug WITHOUT regex


Solution

  • 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 :)