I am relatively new to Django. I've set up my URLs in my core/urls.py file this way and I do get a 404 error when I opened localhost:8000/posts/
on the browser. Code is shown here
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<slug:slug>/', views.SingleView.as_view(), name='single'),
path('posts/', views.PostsView.as_view(), name='posts'),
]
However, everything works fine when I reverse the slug
and posts/
to make the posts
come before the slug
. Like this:
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('posts/', views.PostsView.as_view(), name='posts'),
path('<slug:slug>/', views.SingleView.as_view(), name='single'),
]
Please help me figure it out.
That's how Django (and most other frameworks) work. When a request comes in, Django will check the routes that you specified and it uses the same order that you specified them. So in your first example, ''
is the first one and then '<slug:slug>/'
and 'posts/'
after that. this means that every time a request comes in, Django will check for routes on that order. basically how a for loop works:
Example URL: yoursite.com/posts/
Path: "posts/"
routes = ["", "<slug:slug>/", "posts/"]
path = "posts/"
for route in routes:
if route == path:
# use view for this route
return
So in this case, it will match with index 1 which is <slug:slug>/
, and returns the view specified for it.
Now to understand why <slug:slug>/ == "posts/"
returns True
you need to understand what slug
means in Django:
slug - Matches any slug string consisting of ASCII letters or numbers, plus the hyphen and underscore characters. https://docs.djangoproject.com/en/3.1/topics/http/urls/#example
So it will accept any path that matches those requirements and posts/
actually matches those requirements. Django doesn't check any other routes if it finds a match so it will never gets to path('posts/', views.PostsView.as_view(), name='posts')
because '<slug:slug>/'
has higher priority being in the smaller index over 'posts/'
. And you probably check for that slug in your models which isn't there so it will return a 404.
By changing the route order, you change the routes to ["", "posts/", "<slug:slug>/"]
. Now "posts/"
has higher priority and django will use PostsView
.