Search code examples
pythondjangodjango-viewsdjango-urlsdjango-3.1

Django page not found (404) in my core/urls.py file. Only works when the urls are rearranged


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.


Solution

  • 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.