0

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.

Ivan Starostin
  • 8,798
  • 5
  • 21
  • 39
  • 1
    your urls.py is like a list of urls. When a request is made it starts at the top and goes down each element. The first one where the pattern matches gets executed. since "slug" can be everything, "posts" wont be loaded because it enters the views.SingleView.as_view() and treats "posts" as a slug. since your DB has no object with the slug "posts" it sends back a 404. Either rearrange the order or set a prefix for the slug url like "/posts/ – hansTheFranz Mar 10 '21 at 12:37
  • @hansTheFranz I sorry to say this. I'm totally lost with your explanation – sharhan-alhassan Mar 10 '21 at 12:43
  • 1
    Whenever a user requests a URL django goes to "urls.py" and checks "is this url listed here?" if any url matches the one the user requested -> the view will be loaded. In your special case the "slug" url is generic. ANY word will be seen as a slug. So if someone opens "yoursite.com/peter" the word "peter" is the slug. ANYTHING can be a slug and when you open "yoursite.com/posts" the word "posts" is treated like a slug. and will send it to `SingleView` where 404 is returned because `Posts.objects.get(slug=slug)` is None. Read into MVC and how django processes URLs. There are some good tutorials – hansTheFranz Mar 10 '21 at 12:54
  • @hansTheFranz Thanks a lot for the help – sharhan-alhassan Mar 10 '21 at 15:34

1 Answers1

0

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.

Navid Zarepak
  • 4,148
  • 1
  • 12
  • 26