How does Django's class-based ListView work?
Filed in: Django
April 20, 2012
Check it out, there's nothing to it, or is there?
BaseListView and mixes
MultipleObjectTemplateResponseMixin. Let's visualize how that works.
This is pretty straightforward. We know what TemplateResponseMixin does, so we can skip that.
However, look at
get_template_names() in that mixin. It looks at the type of object we pass to it, and tries to automatically come up with a template name. So for instance, if your app is 'blog' and your model is 'Post', unless you tell it otherwise, Django assumes the template you are using is going to be
We already know that a GET or HEAD request will hit this
get method. So let's see what it is doing here.
- Assigns the result of
self.object_list. (We'll get to this in a minute)
- Checks to see if we allow an empty queryset and raises a 404 if it does.
- Calls to
object_list(which is the queryset result) to the context.
get_queryset paginate_queryset get_paginate_by get_paginator get_allow_empty get_context_object_name get_context_data
allow_empty queryset model paginate_by context_object_name paginator_class
Okay, this one is a bit of a beast.
- It looks for a class property called
queryset. If that exists, there's not a lot to do, so it returns it.
None, it looks for a class property called
model. If that exists it calls
- If neither of these exist, raise an error.
I'm going to gloss over
get_paginator. But you should read them.
This sets up paginator, page_obj, is_paginated and object_list in the context. It also checks to see if the queryset should be adjusted for pagination.
One neat thing here is you can set a custom name for the object list in the context. You do this by specifying
self.context_object_name. If that's set you can iterate over something like
posts instead of the default
object_list. This isn't a huge deal, but can help to make things more descriptive for designers working in the templates.
There are a few ways we can use this list view. Let's look at a couple.
from django.views import generic from myproject.apps.blog import models as blog_models urlpatterns = patterns('', url(r'^blog/$', generic.ListView.as_view( queryset=blog_models.Post.objects.all(), paginate_by=25), name='blog-list-view') )
Here we setup
paginate_by. Django is going to assume we are using a template called
Let's do something a bit more complex. Say we want to show a list of only the posts from the currently logged in user. We wrote a LoggedInMixin last time to help us out, so we'll use that as well.
// views.py from django.views import generic from myproject.apps.users.models import LoggedInMixin from myproject.apps.blog.models import Post class PostListView(LoggedInMixin, generic.ListView): template_name = 'blog/post_list.html' paginate_by = 25 context_object_name = 'posts' def get_queryset(self): return Post.objects.filter(author=self.request.user) // urls.py from myproject.apps.blog.views import PostListView urlpatterns = patterns('', url(r'^blog/$', PostListView.as_view()), name='blog-list-view')
Still, we have an amazingly powerful view that before could have taken quite a bit of code down to 6-7 lines. Pretty sick, isn't it?
If you're grooving on this, use iPDB or PDB to go through some of these views and trace them.