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:
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.