Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

Python Django Forms Model Forms Editing Questions

django.db.utils.OperationalError: table courses_question has no column named prompt

Please help me! I can't figure out what I am doing wrong. I keep getting the following error when I try to make a new question:

[15/Aug/2018 07:54:56] "POST /courses/3/create_question/mc/ HTTP/1.1" 500 160596
Internal Server Error: /courses/3/create_question/mc/
Traceback (most recent call last):
  File "/home/awake/anaconda3/lib/python3.6/site-packages/django/db/backends/utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)
  File "/home/awake/anaconda3/lib/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 303, in execute
    return Database.Cursor.execute(self, query, params)
sqlite3.OperationalError: table courses_question has no column named prompt

models.py

from django.db import models
from django.urls import reverse


class Course(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    title = models.CharField(max_length=255)
    description = models.TextField()

    def __str__(self):
        return self.title


class Step(models.Model):
    title = models.CharField(max_length=255)
    description = models.TextField()
    order = models.IntegerField(default=0)
    course = models.ForeignKey(Course, on_delete=models.CASCADE)

    class Meta:
        abstract = True
        ordering = ['order',]

    def __str__(self):
        return self.title

class Text(Step):
    content = models.TextField(blank=True, default='')

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

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, on_delete=models.CASCADE)
    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


class Answer(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    order = models.IntegerField(default=0)
    text = models.CharField(max_length=255)
    correct = models.BooleanField(default=False)

    class Meta:
        ordering = ['order', ]

    def __str__(self):
        return self.text

forms.py

from django import forms

from . import models


class QuizForm(forms.ModelForm):
    class Meta:
        model = models.Quiz
        fields = [
            'title',
            'description',
            'order',
            'total_questions',
        ]


class TrueFalseQuestionForm(forms.ModelForm):
    class Meta:
        model = models.TrueFalseQuestion
        fields = ['order', 'prompt']


class MultipleChoiceQuestionForm(forms.ModelForm):
    class Meta:
        model = models.MultipleChoiceQuestion
        fields = [
            'order',
            'prompt',
            'shuffle_answers'
        ]


class AnswerForm(forms.ModelForm):
    class Meta:
        model = models.Answer
        fields = [
            'order',
            'text',
            'correct'
        ]

views.py

@login_required
def quiz_create(request, course_pk):
    course = get_object_or_404(models.Course, pk=course_pk)
    form = forms.QuizForm()

    if request.method == 'POST':
        form = forms.QuizForm(request.POST)
        if form.is_valid():
            quiz = form.save(commit=False)
            quiz.course = course
            quiz.save()
            messages.add_message(request, messages.SUCCESS,
                                 "Quiz added!")
            return HttpResponseRedirect(quiz.get_absolute_url())
    return render(request, 'courses/quiz_form.html', {'form': form, 'course': course})


@login_required
def quiz_edit(request, course_pk, quiz_pk):
    quiz = get_object_or_404(models.Quiz, pk=quiz_pk, course_id=course_pk)
    form = forms.QuizForm(instance=quiz)
    if request.method == 'POST':
        form = forms.QuizForm(instance=quiz, data=request.POST)
        if form.is_valid():
            form.save()
            messages.success(request, "Updated {}.".format(form.cleaned_data['title']))
            return HttpResponseRedirect(quiz.get_absolute_url())

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


@login_required
def create_question(request, quiz_pk, question_type):
    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
    })

@login_required
def edit_question(request, quiz_pk, question_pk):
    question = get_object_or_404(models.Question,
                                 pk=question_pk, quiz_id=quiz_pk)
    if hasattr(question, 'truefalsequestion'):
        form_class = forms.TrueFalseQuestionForm
        question = question.truefalsequestion
    else:
        form_class = forms.MultipleChoiceQuestionForm
        question = question.multiplechoicequestion
    form = form_class(instance=question)

    if request.method == 'POST':
        form = form_class(request.POST, instance=question)
        if form.is_valid():
            form.save()
            messages.success(request, "Updated question")
            return HttpResponseRedirect(question.quiz.get_absolute_url())
    return render(request, 'courses/question_form.html', {
        'form': form,
        'quiz': question.quiz
    })

urls.py

from django.urls import path, re_path

from . import views

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

quiz_form.html

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

{% block title %}{{ form.instance.title|default:"New Quiz" }} | {{ course.title }} {{ block.super }}{% endblock %}

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

{% block content %}
<div class="row columns">
    {{ block.super }}
    <h1>{{ form.instance.title|default:"Make a new quiz" }}</h1>
    <form method="POST" action="">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" class="button" value="Save">
    </form>
</div>
{% endblock %}

question_form.html

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

{% block title %}{{ form.instance.prompt|default:"New question" }} | {{ quiz.course.title }} {{ block.super }}{% endblock %}

{% 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 %}

{% block content %}
<div class="row columns">
    {{ block.super }}
    <h1>{{ form.instance.prompt | default:"Make a new question." }}</h1>
    <form method="POST" action="">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" class="button" value="Save">
    </form>

</div>
{% endblock %}

quiz_detail.html

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

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

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

{% block content %}
    <div class="row columns">
        <article>
            {{ block.super }}
            <h1>{{ step.title }}</h1>
            <ul class="no-bullet">
                {% for question in step.question_set.all %}
                <li>
                    <h2>{{ question.prompt }}</h2>
                    {% for answer in question.answer_set.all %}
                        <div class="callout {% if answer.correct %}success{% endif %}">{{ answer.text }}</div>
                    {% endfor %}
                    {% if user.is_authenticated %}
                        <a href="{% url 'courses:edit_question' question_pk=question.pk quiz_pk=step.pk %}" class="button">Edit</a>
                    {% endif %}
                </li>
                {% endfor %}
            </ul>
        </article>
        {% if user.is_authenticated %}
        <hr>
        <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</a>
        <a href="{% url 'courses:create_question' quiz_pk=step.pk question_type="tf" %}" class="button">New True/False</a>
        {% endif %}
    </div>
{% endblock %}

1 Answer

I think it is telling you that it can't find a column called "Prompt" in the table for question possibly because you haven't marked your Question model as abstract. You need to mark the Question model is abstract if you are going to extend it in your multiple choice and true/false models

class Meta:
    abstract = True

https://docs.djangoproject.com/en/2.1/topics/db/models/

If you add that to your Questions class' Meta class, then make migrations and migrate your DB, it should have the column for "prompt".

Try that and see if it helps!

Hi,

Thank you so much for your answer! I tried making Question abstract and then I tried makemigrations but then I got the following error:

SystemCheckError: System check identified some issues:

ERRORS:
courses.Answer.question: (fields.E300) Field defines a relation with model 'Question', which is either not installed, or is abstract.
courses.Answer.question: (fields.E307) The field courses.Answer.question was declared with a lazy reference to 'courses.question', but app 'courses' doesn't provide model 'question'.

Hey, sorry I should have made sure I knew what I was talking about before I posted that!

I mistakenly thought you needed to abstract your Question model, but this is supposed to use Multi-Table Inheritance, so please forget what I said before and remove the "abstract" attribute from your Question model.

Instead, once you remove that, try makemigrations and migrate again and let me know if the issue persists! The issue may just have been that something wasn't synched up.

Cheers