Chanwoo Ahn
Chanwoo Ahn

Reputation: 334

django template url tag with empty argument

I'm trying to make an article list view of my homepage which works in two ways.
Here is a part of my app url.py:

path('article_list/<slug:tag>', views.article_list, name='article_list'),

I would like to make this one URL cover both all articles case and tag-specified article case, by making the view shows all articles if slug: tag is ''.
Here is a part of my view.py:

def article_list(request, tag):
    if tag == '':
        articles = get_list_or_404(Article)
    else:
        articles = get_list_or_404(Article, tags__name__in=[tag], publish=True)

    context = {
        'tag': tag,
        'articles': articles,
    }
    return render(request, 'blog/article-list.html', context)

Every thing works just fine execpt rethriving all article URL on html with django template {% url %} tag.
I want something like this:

href="{% url 'article_list' '' %}"

How can I make the URL with an empty argument(in this case, empty slug)?

Upvotes: 4

Views: 1447

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476709

The reason this does not work is because the <slug:…> path converter [Django-doc] matches only non-empty subsequences of the path. Indeed, the class is defined as [GitHub]:

class SlugConverter(StringConverter):
    regex = '[-a-zA-Z0-9_]+'

There are basically two options here: generate two paths (one for tags, one without tags), or implement a path converter/use a regex.

Option 1: two paths

# app/urls.py

from django.urls import path
from app import views

urlpatterns = [
    path('article_list/', views.article_list, name='article_list', kwargs={'slug': ''}),
    path('article_list/<slug:tag>', views.article_list, name='article_list'),
]

Option 2: implement a path converter

It might be worth here to implement a path converter yourself:

# app/converters.py

from django.urls.converters import SlugConverter

class EmptyOrSlugConverter(SlugConverter):
    regex = '[-a-zA-Z0-9_]*'

Then you can register this and use the <emptyorslug:…> path converter:

# app/urls.py

from django.urls import path, register_converter
from app import converters, views

register_converter(converters.EmpotyOrSlug, 'emptyorslug')

urlpatterns = [
    path('article_list/<emptyorslug:tag>', views.article_list, name='article_list'),
]

Note that you can slightly improve the readability of your view with:

def article_list(request, tag):
    if not tag:
        articles = get_list_or_404(Article, tags__slug=tag, publish=True)
    else:
        articles = get_list_or_404(Article)

    context = {
        'tag': tag,
        'articles': articles,
    }
    return render(request, 'blog/article-list.html', context)

Note that you probably want to filter on the slug of the tag, not the name.

Upvotes: 2

Related Questions