Generic CBVs can be limiting. Using the basic View class frees us of such restrictions.
- [Instructor] Hello, and welcome back to creating custom class-based views from mastering Django web development by Pack Publishing. In this video, we're going to look at using the generic.View to build fully custom class-based views. We're also going to learn how to define custom GET and POST methods on a class-based view, and then we'll end out this section by completing Django's tutorial and rebuilding the vote and result views as class-based views. So, why should we use the generic.View instead of one of the others? Well, first off, it's the most basic class-based views.
It defines the fewest methods, usually only a few, like dispatch and as_view, and it's extremely easy to extend using HTTP verbs like GET, POST, PUT, or DELETE. You can also define more than one as well. So now let's look at some code. So back in our views file I've created a new class called VoteView. Inside of this class I've defined two functions: get_queryset and post. All get_queryset does is it takes a choice_id and looks up the ID through the choice model.
The post method takes two parameters: a request, and a primary key, shortened to pk. The pk will be the question_id. We then extract the choice_id through the POST.get method, which just gets information from a POST request from a form. We then attempt to use get_queryset using the choice_id to get the choice object. If this fails with a KeyError, or a Choice.DoesNotExist, which means the model couldn't find a choice with that ID, it returns a redirect to the polls:detail view, passing the question_id as the parameter.
Otherwise we increment the vote by one, save the query, and redirect to a new results view passing the question_id as the primary key. Now let's look at how the Django tutorial handled this with a function-based view. Here's the code provided by djangoproject.com. As you can see there's a lot of difference. First and foremost, it's not as obvious where we're getting our data from. The get_object_or_404 only has two outcomes, either we get the object, or we 404.
There's no way to control what the user ends up seeing. To do this we'd have to write a lot of extra code, as opposed to the class-based view, that just redirects. Secondly, whenever the functional view fails to get a choice back, or runs into a KeyError, it returns a render instead of redirecting back to another. This can also be problematic, as now we have our two functional views that are returning the same render function with the same template.
There is no real reason to do that. And lastly, the functional vote view accepts any kind of HTTP request, either GET, HOST, PUT, or DELETE. If we wanted to add the kind of protection that the class-based view provides, we would have to wrap everything in a large if statement that looked like this. And that doesn't look nearly as nice, or as nearly as readable as simply defining a def POST function.
So now we won't be using the vote functional view anymore. So let's just comment it out for now. The next view we're going to be talking about is the ResultsView. This view inherits from both the TemplateResponseMixin and the generic.View. TemplateResponseMixin can be imported from the django.views.generic.base. This Mixin provides a couple helpful functions.
The main one we'll be using is the self.render_to_response. So let's take a look at this view. The first thing we've done is defined a template_name variable pointing to polls/results.html. This may look similar to our other views, like the detail and list view, and that's because this will work exactly the same because of the TemplateResponseMixin. Next we've defined get_queryset that takes a question_id that uses the question_id to look up a question by its primary key.
And lastly we've defined a get method that takes a request and a primary key. It then calls the get_queryset method, wraps it all up in a nice little context variable, and calls render_to_response. The self.render_to_response works just like django.shortcut render_to_response. So now let's take a look at how this works on the server. As you can see, we still have one poll: 'Do you like Django?' Whenever we click on it, we're taken to the detailed view, and we're presented with a form.
Select 'definitely', and hit 'vote'. As you can see, we're now on the results view, and the increment for definitely has increased by one. We can go back to the detailed view, to ensure that again. As you can see, we're actually going to the vote view, and then immediately being redirected to the results view after the vote's tallied out.
- Extending class-based views
- Building a REST API
- Working with GraphQL
- Building a basic schema
- Optimizing your environment
- Working with Pipenv
- How Django handles testing
- Securing the Django admin