Django: NoReverseMatch not a valid function

Viewed 122

Running my local Django dev server (2.2) in a virtual environment, I encounter a trace back. The essential keywords that are part of this error include “django.urls.exceptions.NoReverseMatch” and “not a valid view function or pattern name.”

I’m doing a code-along kinda Udemy course by Nick Walter. Part of the course material involves writing a rudimentary blog using Django. I’m close to the end of Nick’s blog module.

I figure I have referred to a function inaccurately somewhere or perhaps I have misconfigured my urlpattern. There are a few other SO members who have encountered similar errors with the resolution typically involving correcting a typo. I've tried removing the pluralization of my post_details views function. I’ve tried variations in my urls.py with different combinations of regular expressions (and without). I feel like I am overlooking something trivial here.

It’s gotten to the point where I am comparing my code to the course instructor’s end-of-module source code and I can’t for the life of me figure out what I am doing wrong.

Here is my code:

urls.py:

from django.urls import path, re_path
# from . import views
from posts.views import *
from redactors.views import *
from counters.views import *
from django.conf.urls.static import static
from django.conf import settings

urlpatterns = [
  path('admin/', admin.site.urls),
  path('', home, name='home'),
  path('result/', result, name='result'),
  path('seth/', counters, name='seth'),
  path('james/', posts, name='james'),
  re_path(r'^posts/(?P<post_id>[0-9]+)/$', post_details, name='james'),
  path('simon/', redactors, name='simon'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 

posts/views.py:

from django.shortcuts import redirect, render, get_object_or_404
from .models import Post

def posts(request):
    posts = Post.objects.order_by('-pub_date')
    return render(request, 'posts/james.html', {'posts':posts})

def post_details(request, post_id):
    post = get_object_or_404(Post,pk=post_id)
    return render(request, 'posts/detailed.html', {'post':post})

posts/templates/posts/detailed.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    
    <title>Neptune Blog</title>
    
  </head>
  <body>
  
  <h1>{{ post.title }}</h1>

      <a href="{% url 'post_details' post.id %}"><h1>{{ post.title }}</h1></a>
      <h4><span class="glyphicon glyphicon-time" aria-hidden="true"></span> {{ post.pub_date_pretty }}</h4>
      <br />
      <img src="{{ post.image.url }}" class="img-responsive center-block" style="max-height:300px;" />
      <br />
      <p>{{ post.body }}</p>
      <br />
      <br />
</body>
</html>

I’m expecting my blog to load and render properly.

Here is a screenshot of the Django debugger showing the traceback in my web browser.

Here is the traceback from the shell in full:

File "/home//dev/projects/python/2018-and-2019/CC_Redact_Iter2/venv/lib/python3.7/site-packages/django/urls/resolvers.py", line 660, in _reverse_with_prefix

raise NoReverseMatch(msg)

django.urls.exceptions.NoReverseMatch: Reverse for 'post_details' not found. 'post_details' is not a valid view function or pattern name.

1 Answers

If we take a look at the urls.py, we see:

# urls.py

urlpatterns = [
    # ...
    re_path(r'^posts/(?P<post_id>[0-9]+)/$', post_details, name='james'),
    # ...
]

so the name of the view is james, not post_details. There are thus two options:

  1. use james when you refer to the view; or
  2. change the name of the view to post_details

Use james as the name of the view

You thus should write the url as:

<a href="{% url 'james' post_id=post.id %}"><h1>{{ post.title }}</h1></a>

Change the name of the view

Or you can change the name of the view of course (change name='james' to name='post_details', since that is likely a better name). In that case you thus will need to change everything that already refers to james to the new view:

# urls.py

urlpatterns = [
    # ...
    re_path(r'^posts/(?P<post_id>[0-9]+)/$', post_details, name='post_details'),
    # ...
]

Here it is even more important to change the name of the view, since right now there are two views that have as name 'james', which can result in a lot of confusion.

Related