Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python Django Forms Model Forms Handling Multiple Form Classes

Jonathan Mitten
PLUS
Jonathan Mitten
Courses Plus Student 11,197 Points

Reverse for 'quiz' with keyword arguments '{'course_pk': 1}' not found. 1 pattern(s) tried: ['courses/(?P<course_pk>\\d+

I see some folks who have run up against this issue in much earlier video chapters, and I've tried to follow the debug logic to the best of my ability. I'm just not able to solve this issue. I've even gone so far as to dig into the final version of the project (in the downloads section) and cross-referenced my templates, models, forms, views, and URLs, and the project has been working up to this chapter (https://teamtreehouse.com/library/django-forms/model-forms/handling-multiple-form-classes).

The big question is how can this error be debugged? It seems like a big question on StackOverflow. Here is my quiz_detail.html template, which throws the error after a "New Multiple Choice Question" or "New True / False Question" button click, so the error actually occurs on the URL http://site_root/courses/2/create_question/mc/ (I'm running on my own dev server):

{% extends "courses/layout.html" %}
{% load course_extras %}

{% block title %}
    {{ step.title }} | {{ step.course.title }} {{block.super }}
{% endblock title %}

{% block breadcrumbs %}
    <li><a href="{% url 'courses:detail' pk=step.course.pk %}">{{ step.course.title }}</a></li>
{% endblock breadcrumbs %}

{% block content %}

    <div class="row columns">
        <article>
            {{ block.super }}
            <h1>{{ step.title }}</h1>
            Quiz questions here
        </article>
        {% if user.is_authenticated %}
            <hr />
            <div>Debug: step.pk is: {{step.pk}} </div>
            <a
                href="{% url 'courses:edit_quiz' course_pk=step.course.pk quiz_pk=step.pk %}"
                class="button">
                Edit
            </a>
            <a
                href="{% url 'courses:create_question' quiz_pk=step.pk question_type='mc' %}"
                class="button">
                New Multiple Choice Question
            </a>
            <a
                href="{% url 'courses:create_question' quiz_pk=step.pk question_type='tf' %}"
                class="button">
                New True/False Question
            </a>
        {% else %}
            <p>You're not auth'ed</p>
        {% endif %}
        <div>quiz_detail.html</div>
    </div>
    <!-- /.row columns -->
{% endblock content %}

The urls.py looks like this:

"""URLS."""
from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^$', views.course_list, name='list'),
    url(r'(?P<course_pk>\d+)/t(?P<step_pk>\d+)/$', views.text_detail,
        name='text'),
    url(r'(?P<course_pk>\d+)/q(?P<step_pk>\d+)/$', views.quiz_detail,
        name='quiz'),
    url(r'(?P<course_pk>\d+)/create_quiz/$', views.quiz_create,
        name='create_quiz'),
    url(r'(?P<course_pk>\d+)/edit_quiz/(?P<quiz_pk>\d+)/$', views.quiz_edit,
        name='edit_quiz'),
    url(r'(?P<quiz_pk>\d+)/create_question/(?P<question_type>mc|tf)/$',
        views.create_question, name='create_question'),
    url(r'(?P<pk>\d+)/$', views.course_detail, name='detail'),
]

and the view called by the create_question URL is here:

@login_required
def create_question(request, quiz_pk, question_type):
    """Create question."""
    quiz = get_object_or_404(models.Quiz, pk=quiz_pk)
    if question_type == 'tf':
        form_class = forms.TrueFalseQuestionForm
    else:
        form_class = forms.MultipleChoiceQuestionForm
    form = form_class()

    if request.method == 'POST':
        form = form_class(request.POST)

        if form.is_valid(): 
            question = form.save(commit=False)
            question.quiz = quiz
            question.save()

            messages.success(request, "Added question")
            return HttpResponseRedirect(quiz.get_absolute_url())

    return render(request, 'courses/question_form.html', {
        'quiz': quiz,
        'form': form
    })

and the models I believe are in the scope of this issue, here:

class Quiz(Step):
    total_questions = models.IntegerField(default=4)

    class Meta:
        verbose_name_plural = 'Quizzes'

    def get_absolute_url(self):
        return reverse('courses:quiz', kwargs={
            'course_pk': self.course_id,
            'step_pk': self.id
        })

class Question(models.Model):
    quiz = models.ForeignKey(Quiz)
    order = models.IntegerField(default=0)
    prompt = models.TextField()

    class Meta:
        ordering = ['order',]

    def get_absolute_url(self):
        return self.quiz.get_absolute_url()

    def __str__(self):
        return self.prompt

class MultipleChoiceQuestion(Question):
    shuffle_answers = models.BooleanField(default=False)


class TrueFalseQuestion(Question):
    pass

Going through the traceback, the legible error message comes from:

Template error:
In template /path/to/learning_site/templates/layout.html, error at line 0
   Reverse for 'quiz' with keyword arguments '{'course_pk': 1}' not found. 1 pattern(s) tried: ['courses/(?P<course_pk>\\d+)/q(?P<step_pk>\\d+)/$']   1 : <!doctype html>
   2 : {% load static from staticfiles %}
   3 : {% load course_extras %}
   4 : <html class="no-js" lang="en">
   5 :     <head>
   6 :         <meta charset="utf-8">
   7 :         <meta name="viewport" content="width=device-width, initial-scale=1.0">
   8 :         <title>{% block title %}{% endblock %}</title>
   9 :         <link rel="stylesheet" href="{% static 'css/foundation.min.css' %}">
   10 :         <link rel="stylesheet" href="{% static 'css/layout.css' %}">

This all seems to match with the "Final" version provided by the download. I'm going to see if I can track down the changes by finding the chapter directory and going through to copy and paste, and if I find the answer, I'll post it here.

How can a developer debug the NoReverseMatch error, precisely?

1 Answer

Jonathan Mitten
PLUS
Jonathan Mitten
Courses Plus Student 11,197 Points

I found the culprit, and it's not in the files or code I posted. It was in the question_form.html and was buried in one of the breadcrumbs.

For those interested, the old file read:

{% extends "courses/layout.html" %}
{% load course_extras %}

{% block title %}
    New question for {{quiz.title}} | {{ quiz.course.title }} {{block.super}}
{% endblock title %}

{% block breadcrumbs %}
    <li><a href="{% url 'courses:detail' pk=quiz.course.pk %}">{{ quiz.course.title }}</a></li>
    <li><a href="{% url 'courses:quiz' course_pk=quiz.course.pk %}">{{ quiz.title }}</a></li>
{% endblock breadcrumbs %}
{% block content %}
    <div class="row columns">
        {{ block.super }}
        <h1>Make a new question</h1>
        <form action="" method="POST">

            {% csrf_token %}
            {{ form.as_p }}
            <input type="submit" class="button" value="Save" />
        </form>
    </div>
    <!-- /.row columns -->
{% endblock content %}

<div>question_form.html</div>

and the issue was in the second breadcrumb, where courses:quiz is passing only the course_pk argument to the URL. The step_pk is of course also required. The file now reads:

{% extends "courses/layout.html" %}
{% load course_extras %}

{% block title %}
    New question for {{quiz.title}} | {{ quiz.course.title }} {{block.super}}
{% endblock title %}

{% block breadcrumbs %}
    <li><a href="{% url 'courses:detail' pk=quiz.course.pk %}">{{ quiz.course.title }}</a></li>
    <li><a href="{% url 'courses:quiz' course_pk=quiz.course.pk step_pk=quiz.pk %}">{{ quiz.title }}</a></li>
{% endblock breadcrumbs %}
{% block content %}
    <div class="row columns">
        {{ block.super }}
        <h1>Make a new question</h1>
        <form action="" method="POST">
            {% csrf_token %}
            {{ form.as_p }}
            <input type="submit" class="button" value="Save" />
        </form>
    </div>
    <!-- /.row columns -->
{% endblock content %}

<div>question_form.html</div>

and the form renders as expected.

It is spectacularly irritating that the file question_form.py is not mentioned in any of the debug messaging, but I suppose the answer to my question of "how do we debug NoReverseMatch" is to search the project files for the term courses:quiz and check each line where it's included. In my case, it's the only file outside of the models.py that has that url - I suppose that's the DRY method in action there, anyway.