Skip to main content

Using Models in Views

Now let's apply what we just learned to our views. Let's start with the courses() view which contained this code:

def courses(request: HttpRequest) -> HttpResponse:
conn = sqlite3.connect("courses.db")
cursor = conn.cursor()
cursor.execute("SELECT name, enrolled_students FROM courses")
courses = cursor.fetchall()
conn.close()
ctx = {"courses": courses}
return render(request, "myapp/courses.html", ctx)

Since we will now do all interaction with the database through models, it is no longer necessary to use the sqlite3 module. So first remove this line:

import sqlite3

And add this one:

from .models import Course

Now the view should look simply like this:

def courses(request: HttpRequest) -> HttpResponse:
courses = Course.objects.all()
ctx = {"courses": courses}
return render(request, "myapp/courses.html", ctx)

But since the return value of all() is not a list but a QuerySet, let's also update the templates/myapp/courses.html template and change this loop:

     {% for name, enrolled_students in courses %}
<tr>
<td>{{ name }}</td>
<td>{{ enrolled_students }}</td>
</tr>
{% endfor %}

For this one:

     {% for course in courses %}
<tr>
<td>{{ course.name }}</td>
<td>{{ course.enrolled_students }}</td>
</tr>
{% endfor %}

Now visit http://127.0.0.1:8000/myapp/courses and you should see:

List of courses

If you still have the courses_json() view, make also the proper changes. It should now look like this:

def courses_json(request: HttpRequest) -> JsonResponse:
response = JsonResponse(list(Course.objects.values()), safe=False)
return response

JsonResponse requires the argument to be a serializable object. QuerySet objects are not serializable, but Python lists are. Hence we convert the values retrieved through the model using list(). Additionally, items in that list must be dictionaries, not Course instances; that's why we use values() instead of all().

Let's also update the course() view, which retrieves information for a single course, to look like this:

from django.http import Http404

# ...

def course(request: HttpRequest, course_name: str) -> HttpResponse:
try:
course = Course.objects.get(name=course_name)
except Course.DoesNotExist:
raise Http404
ctx = {"course": course}
return render(request, "myapp/course.html", ctx)

And in the templates/myapp/courses.html template change indexes to field names:

<h1>Course information for {{ course.name }}</h1>
Students: {{ course.enrolled_students }}

Finally, update the new_course() view, which was responsible for adding the fields received by the form to the database, to get rid of sqlite3-related code and use our model instead:

def new_course(request: HttpRequest) -> HttpResponse:
if request.method == "POST":
form = forms.CourseForm(request.POST)
if form.is_valid():
Course.objects.create(
name=form.cleaned_data["name"],
registered=form.cleaned_data["registered"]
)
return HttpResponseRedirect(reverse("courses"))
else:
form = forms.CourseForm()
ctx = {"form": form}
return render(request, "myapp/new_course.html", ctx)

In the next chapter we will see a more efficient method to work with forms to models.