Class-based Views: Past, Present and Future

This video features Dr. Russell Keith-Magee at DjangoCon US 2014 in Portland, Oregon, USA.

Class-based Views: Past, Present and Future
0:39:36
Published September 13, 2014
4,120 views

By, Russell Keith-Magee
One of the big changes in Django 1.3 was the introduction of Class-Based Views. Opinion on them is strongly divided; some love them, some hate them.

Help us caption & translate this video!

http://amara.org/v/FNEo/

Summary

Russell Keith-Magee explains that Django’s class-based views were created to extend the reusable generic-view patterns introduced in 2005. A class-based view is fundamentally a class that dispatches HTTP verbs such as GET and POST to methods, while class-based generic views add reusable behavior through inheritance and mixins, making it easier to insert custom form processing, permissions, and response formats. He acknowledges that the generic-view implementation is difficult to understand, partly because its documentation initially explained details without the larger picture, but argues that the underlying class-based approach remains valuable. The older generic views solved the needs of early Django sites, whereas modern applications need API-driven interfaces, richer JavaScript front ends, multiple actions and forms, and more flexible composition; he suggests using class-based views as a foundation for exploring those newer interfaces, potentially outside Django core at first.

Key takeaways

  • Django’s original generic views reduced repetitive code for common tasks such as displaying objects, handling forms, and creating date archives.
  • Class-based views dispatch requests by HTTP method and provide extension points through subclassing and mixins.
  • Class-based generic views are powerful but have a steep learning curve because their behavior is distributed across many mixins and classes.
  • Custom behavior such as form processing, permission checks, and JSON responses can be added by overriding methods or composing mixins.
  • The generic views designed for 2005 do not fully match modern applications built around APIs, JavaScript interfaces, multiple forms, and dynamic page behavior.
  • Class-based views could provide the framework for richer, extensible user interfaces, with Django’s admin offered as an existing example.

Summarised automatically from the transcript.

Chapters

  1. 0:00 Introduction to Class-Based Views Russell Keith-Magee frames the talk as a big-picture examination of why Django has class-based views and where they should go.
  2. 1:52 Django’s Generic Function-Based Views The talk traces Django’s early generic views and shows how they reduced repeated code for common operations such as editing objects.
  3. 6:26 Limits of Function-Based Generic Views Configuration arguments and callable hooks become unwieldy when developers need custom forms, permissions, or logic at precise points in the request flow.
  4. 8:45 The Case for Class-Based Views Subclassing and mixins are introduced as a cleaner way to provide extension points and compose reusable behavior.
  5. 9:32 The Class-Based View Controversy The speaker reviews the long development history of Django’s class-based views and the confusion caused by their implementation, architecture, and documentation.
  6. 11:54 Class-Based Views and Generic Views The talk distinguishes the basic HTTP-method-dispatch mechanism from the higher-level class-based generic views built on top of it.
  7. 16:32 The Ravioli Architecture The speaker explains the non-obvious deployment design and the criticism that generic views require understanding a complex network of mixins.
  8. 18:51 Generic View Mixins in Practice An edit view is decomposed into object, form, processing, and template-response mixins to show how the architecture works internally.
  9. 22:02 Customization and Reuse The talk demonstrates how create views, JSON responses, subclassing, and reusable permission mixins extend generic view behavior.
  10. 27:35 Documentation and Future Generic Views The speaker discusses documentation gaps, unresolved conventions, and possible additions such as formset and inline-formset views.
  11. 29:53 Modern Web Application Problems The talk questions whether generic views target today’s needs, including multiple forms, continuous scrolling, Ajax, rich JavaScript interfaces, and multiple page actions.
  12. 32:11 API-Driven User Interfaces APIs are presented as the functional center of modern applications, with user interfaces acting as a presentation layer that can be tested separately.
  13. 34:31 Django Admin as a Model Django’s admin provides evidence that class-based structures can support complex, extensible interfaces, potentially outside Django’s core package.

Transcript

7,684 words · auto-generated Show

Automatically transcribed, so expect mistakes in names and technical terms.

0:21

Okay, so uh Fear, yeah. For those who don't know me, my name is Russell Keith McGee. My day job is as the CTO and co-founder of Trades Cloud We're a cloud provider of services, software services for tradespeople, so plumbers, electricians, people like that. But that, like I said, is my day job. The reason that I'm here is that I've been a core developer on the Django project uh since January of 2006. Uh I've been president of the Django Software Foundation since 2010. Uh the DSF, as I've said in my introduction this morning, is the the IP legal and fundraising armor of the Django project. So if you are interested, if you are if you represent a company that has money, I'd like to talk to you. Because we need your money. But that's why that's not why I'm here today. Today I'm here to talk about class-based views. Class-based views were introduced in Django

1:06

1. 3, uh but have not been, how should I put this, uh, greeted with universal enthusiasm. So let's uh the reason I'm giving this talk, and this is this is a re uh a redo of a talk that I gave about a month ago at uh DjangoCon Australia. Um I want to clear up the mess. What are class-based views? Why do they exist? And why should anyone care? Now I'd like to emphasize this isn't a tutorial. It's an attempt to sort of set the big picture straight. In the hope that by getting the big picture straight, I'll be able to set some of the broader discussions about class-based views that I keep seeing on mailing lists and Twitter and things like that. And try to set those discussions going in a slightly better direction Or at least stop from being quite so badly wrong at sort of the fundamentals that those discussions sometimes get based on.

1:52

So if we're talking big picture, um to explain why it is, what we've got, what we've got, it helps to know where we've come from. And people who ignore history are doomed to repeat it. So cast your mind back to the Halcyon days of 2005. Django was introduced as a as a mechanism for building websites. Views are there for displaying content. There are lots of common patterns in views, and we all like being dry. We like not repeating ourselves So Django had this concept of uh of generic views. They were added because we wanted those properties. They weren't in Django's very original release. They were added on July 25th, 2005, release 304. So about two weeks after Django went public.

2:39

class-based views or fur sorry uh generic views um first landed in in Django's code base. And they were added because we had these common problems we wanted to solve. We want to avoid uh um uh Reproducing the same code over and over again. So you refactor all the code you have, you identify those key patterns. Generic views are there to do things like display a template. Display a single object on a page. Display a list of objects on a page. Display a form with the create, update, and delete kind of consequences of displaying a form. Display an archive of objects based around date. Remember, Django is coming from a journalism or a strong journalism background, so uh a list of articles by date is a common problem that people were trying to solve.

3:25

Those generic views are then wrapped up in sort of a in a in sort of a have a couple of key features. Each view is a function, and you configure that function by passing in arguments to to control how you want that function to operate. So, for example A common, this is sort of a running through the code that you would have written for an edit view for a book. The book, the view, you take a request object and you pass in the ID number of the book you want to edit You do a try catch to or try accept to get the book. The book doesn't exist, return a 404. If it's a post. then we're actually saving the result of the form. Okay, so we need to instantiate the form with the data from the request and the instance of the book that we're trying to edit. Check that the form is valid. Have we provided all the

4:10

information that's required? If it is, then save that form and then redirect to the URL for that particular book. Fantastic. If it's not a post request, then we're going to assume that it's a GET request. Generate the form for just the empty instantiated. Show us what the form should look like. And render. the um that form and the object against some sort of template. Okay, so absolutely boring vanilla code and if you would go and reproduce that code over and over and over again for every object type that you wanted to edit You then take that view and you deploy it into your URL patterns. Okay, define a uh a URL pattern that says I want to edit a book slash book slash ID is where we're going to go. That view is the book view. We're going to call it edit book so we can do a

4:56

URL, a name-based URL lookup. And everything's all nice and happy. We've now got our view for editing a book into our project. But then generic views are there to say, okay, well that code is so cookie-cutter, it's so generic that we can replace that. Instead of talking about your generic, your base idea of an update object We're gonna replace sorry, a book view, we're gonna replace it with this idea of updating an object and configure it to say this update object, well specifically we're gonna be updating books, so we're gonna be looking for books based upon an object ID. The name of the URL is exactly the same. The operation of that function is exactly the same. So instead of having to rewrite the boilerplate to update an object and have that, try catch, if

5:41

post, blah blah blah blah blah. You can just say, well, here is a pre-canned version, or all I need to drop in is the model, which is a book. Fantastic. Which means that okay, we then we have books. We also want to be able to edit authors. The only thing we need to change is where the URL fits. It's slash author instead of slash book. The model that we're configuring is author rather than book. And the name of the URL is edit author instead of edit book. Okay, so that's you know the best best practices of code reuse, find the thing that you're doing over and over and over again, factor it out into something generic, and then configure it on each individual use. Fantastic. So there are some problems though. Okay, so configuration options, the configuration options for that generic function view. Uh

6:26

are limited by the arguments you can pass in. Okay, so it is a function at the end of the day, and we are passing in different arguments to make that thing happen. If you want to uh instantiate a different form, instead of you know a generic model form that's being uh being created, you want to have a very specific form be instantiated. Based upon some sort of property of the object that you've actually retrieved. Well, you can't do that. Do you want to perform some sort of logic after the form has been validated, but before the form is saved? Well, unless you can actually put that logic onto the form 's save method itself, you don't have an entry point to inject that logic. Do you want to pass in extra arguments to the form instantiation? Like for example, really something really simple like the request. Well no, unless you can put it into a decorator

7:11

or something like that. Uh uh you you you ca you can't do it. Do you have common permissions logic that you want to run? Now this view or parts of the you can only access this object if you have a particular permission. Unless you could put that on a decorator You can't do it. So how do we address these problems? Well, okay, you can keep adding new functional entry points, so you can keep saying, okay, well, here's another configuration, another configuration argument, another configuration argument. Or you can say, okay, well that configuration argument can actually be a callable and we can say, okay, well you pass in the function that you want to operate in this particular part of the function and you know get all completely uh you know, meta and and deep like that. Um but at some point what you basically end up doing is poorly implementing half of the features of class inheritance.

7:57

So as an example of what I sort of think I'm talking about here in practice, let's go back to our edit book example view. You've written this code, everyone has written this code at some point. And it's really, really easy to say, well, okay, if the form is valid, before we save, do something. So, you know, we want to prepare the book before we actually save the form. Trivial to do if you've got access to the full view, almost impossible to do if you've got or you can't do it with the generic function-based views that were in Django as of 1. 3. almost impossible to do in a clean way or to extend those function-based views to make that use case really, really easily So, again, we must refactor all the things. And instead of trying to pretend that we're going to implement class-based views by hacking them into a function,

8:45

Let's actually go class-based. Let's actually make these things classes. Subclassing gives you these entry points. You can say, I've got some behavior here. My base behavior is to do nothing or to do a very simple thing, but you can then subclass and add additional behavior that you want. You can override specific pieces of logic. You can use class mix-ins to compose behavior out of other uh out of a collection of objects. And that's essentially what we did for Django 1. 3. Now it turns out that although that's you know the high-level architectural goal, uh in practice it turns out to be not quite that simple. There are lots of little details that needed to get sorted out The discussion around class-based views went on for years. There were several draft attempts made at landing something that was a class-based view.

9:32

Jacob Kaplan Moss tried, Joseph Cochran's tried, Ben Firstman tried. I am sort of the person who stands on the shoulders of giants there. I'm responsible. I'm the person who did the final push and got building on top of the efforts of all those those gentlemen. uh and finally ended up committing something for 1. 3. So the architecture that I committed is most like the work that Ben did. Ben built on top of what Joseph did, Joseph built on top of what Jacob did. Unfortunately, that's where the wheels fell off the whole process. Why did that happen? Well, there's a couple of reasons. Firstly, there's a lot of confusion, or there were a lot, there has been a lot of confusion about what class-based views actually are and what they should be or can be used for.

10:19

There's a lot of that confusion is because there's two very distinct purposes in the code base around this sort of this one commit that was the feature that got landed in 1. 3. And those two very distinct purposes or two very distinct pieces of work were wrapped into a single commit and as a result sort of look like one feature. The second problem is that the implementation isn't necessarily obvious. But that That reason leads to non-obvious complexity. That if you come to the idea for the first time and think, well, why is it being done this way? Isn't this other way? Wouldn't it make a lot more sense and been a lot more obvious? There's a reason why it's not like that. The third uh criticism that sort of has been leveled is uh something that's labelled ravioli code.

11:07

Uh I'll come back to exactly what this means. It's a legitimate criticism that is a legitimate architectural criticism, um and I'll I'll come sort of with an explanation about what that means in a minute. minute. And sort of the the worst problem of all was that the documentation at the time it landed in 1. 3 was uh to use the technical term bad I take full blame for that one. I wrote the documentation such that it was in 1. 3 and uh it was not good documentation by any stretch of the imagination. Uh if anything else it's a it's a uh Uh an abject lesson in the person who writes the code shouldn't be the person who documents it because it was obvious to me, so why isn't it obvious to you guys? Since I landed my original documentation, Mark Tamlin, Danny Greenfeld, a number of others have made some really big contributions, some really big improvements in that area.

11:54

So that situation at least has got a little bit better. But the rest of them still exist. So let's let's go through them and try and clean up this wonderful mess what I made. First off purpose. There are two very distinct pieces of work inside what is the feature of class-based views in Django 1. 3. There are class-based views and there are class-based generic views. When people talk about the commit, it usually gets spoken about in terms of these class-based views, but it is very two very distinct pieces of work. Class-based views are a class-based analog of a view function. It has in embedded in it as part of the implementation a method-based HTTP verb dispatch, and that's it.

12:39

Okay, that is all a class-based view is. It's a way of saying I've got a GET request, implement this piece of functionality, or perform this piece of logic. So, for example, we go back to our, this is this is essentially a class-based view. Okay, it is a class, my class is my view, and it extends a base class called view. You have a get method, guess what? If someone makes a get request, that's the method that gets invoked. If someone makes invokes a post request, the post request is what gets invoked. That is all class-based views are But because it is wrapped around a base class, you bet get a whole bunch of interesting features for free. You could get automated options handling. So, okay, for those of you who sort of aren't that familiar with HTTP or the internals of HTTP The HTTP verb is sort of the request you're the

13:26

name of the thing you're asking the web server to do. The ones that everyone knows about are get and post because it's sort of the read and the write versions of what you're doing on the internet. But there's actually a whole bunch of other useful verbs. Options is the way you ask, what verbs can I use? If I go to this URL, what can I do? Can I do a get? Can I do a post? Can I do a put? Can I do a delete? Can I do a trace? What options are available to me? And you get that, you get options handling for free when you use a class-based view. Because we know if you've defined a get method, therefore you support gets. You also get uh automatic naive head request handling. Now a head request is essentially a way of asking the server, hypothetically, if I was to request this URL, how big would it be?

14:12

Give me the headers, not the content, just the headers for how big this request would be, which is then useful if you're doing sort of performance optimization stuff for a large file delivery or something like that. You can say, how big is this content? And then prepare yourself for how big that content is actually going to be. You can implement that in a function-based view, but in a class-based view, you can do a naive implementation. I just do the you can just do the get request and then don't return the body content. Okay, and you can get that for free. You also automatically get HTTP 405 on any verb that isn't supported. So HTTP 405 is method not allowed. So if you have got a view that you specifically do not want to allow GET requests, just don't implement GET, and anyone who does a GET request on that URL will get HTTP 405. Okay.

14:57

That is particularly useful when you look at a function-based view. Most people write a function-based view. If you don't have any other logic in there, essentially it doesn't matter whether you ask for a get, put, post, options, head, the function-based view will return the same content Which by strictly by the HTTP standard is not what it's supposed to do. Okay, so by doing it, by by having this class-based structure, you can deal with the verbs properly so that you can say, no, you can't do that when someone does something that you're not supposed to be doing. Okay, so that's class-based views. Layered on top of class-based views are class-based generic views. These use the class-based view as a base and then create analogs of all of the old function-based generic views

15:44

in a class-based form. along the way, addressing the limitations of the functional-based approach, so you can have the injection points, you can have the extension points available to you using basic subclassing and mix-ins and all the nice things that you can do with object orientation. without having to go into this um you know trying to invent class-based views by passing in callables or passing in more uh uh more keyword arguments to the configuration of the function. The catch is that by addressing the limitations of that or the approach of the functional approach is that you introduce a whole new body of knowledge. It's now you need to understand the class structure for how generic class-based OIA generic cues work in order to be able to do anything with them That's the ravioli criticism, essentially, and I'll come again I'll come back to exactly why it's uh what

16:32

what that means and why why the term ravioli gets used. The second criticism that gets leveled against class-based views is the implementation choice. The approach that we took is not the obvious one. There are a couple of deployment things you have to do, the way you put a class-based view into your URL patterns Looks a little odd. It looks a little odd because it ha can't be done the obvious way. The w the the obvious approach, just drop the class in the view, has some side effects and would require either an overhead on the part of developers or an overhead on part on the part of the framework that we weren't willing to live with. I won't go through the details of why we've done it the way we've done it. The good news is that Django's wiki Actually, it has a complete teardown of the history of the development of class-based views.

17:17

If you go to that URL, what you will find is these are the ten options we looked at. Here are the pros and cons of every one of them, and this is why we chose this one. Okay, so it's here is the design decision behind why we've got what we've got. So if you're if you're interested, it's certainly worth a read and you can sort of find the find the backstory for why you have to do as view Okay, problem three, ravioli. This phrase came into being because of one of actually one of Django's core developers, a guy named Luke Plant. It's describing the fact that the end product that we've got, or ravioli is sort of as a product, is delicious and tasty and looks lovely in a nice little square packet or triangular packet that's sitting in your plate. The internals, however, don't ask too many questions.

18:03

They're kind of minced and unclear and may contain products you don't want to know about. It also means that there's a price to becoming an expert. Unless you know what the internals were made of, you can't just reproduce it Okay, you end up, yes, with a very attractive outside package, but the inside you need expert knowledge to know how to make the inside of the ravioli. Now this is an entirely legitimate criticism. The uh the the generic class-based views have a very, very complex internal structure. There are lots of entry points. There are there's There's there's all sorts of subclasses and mixings that get that come together to give you the final end product that is an update view. And unfortunately, in order to use them at all for anything other than the most trivial case, you need to understand

18:51

All of it. And that means that the learning curve is hideously steep. That then gets compounded when you have really bad documentation that doesn't explain how to get to the expert position really, really quickly. So yeah, the minimum quantum of knowledge to actually operate is much larger than it should be. So here's my Ravioli cooking class, 101. Let's have a look at our sort of pseudocode version of our original view here. Here's an edit view. We need to get an object. If it's a post, get the form. If the form's valid, handle the form and redirect. Otherwise, we need to handle the failure in some way. If we go back and it was a get request, then we need to get the form and then we need to return a response. So how does this manifest in our generic class-based view?

19:39

That's the collection of mix-ins that go together to make that view happen. So let's step through them one by one. And the internals, right on the inside, we've got a single object mix-in We are dealing with a view that will be returning a single object, or it's dealing with a single object. We need to put that single object into a context so that we can actually deal with that single object. So we have a context mix-in, which is subclassed, into a single object mix-in. which essentially all it does is say I can make sure that I can render a single object on a page. I'm sorry. And that's basically doing the get object part of this picture. I'm dealing with a single object. I need to be able to get an object. Process form view is the bit that says, okay, well this is a view that's dealing with forms.

20:27

That means I need to be able to deal with get, posts, and puts. So let's make sure that I've got a get post and put entry point and that that then defers to whatever it is the rest of this form of this this page is doing. If it's dealing with a single object, lots of objects, I don't know, but I need to be able to do get post and put and we'll defer everything else to subclasses What exactly are we going to do? Well, okay, in this case, we're dealing with a form. The form needs to be handled. And the way we deal with forms, we need to instantiate a form, and then we need to check the form is valid. We need to uh handle the failure and we need to be able to instantiate the form when we have data, when we don't have data. But in particular, this is a form that's been driven by a model, so we need to be able to generate a model-based form. So we have a form mix-in which contributes to the model form mix-in. The model form mix-in knows that it's dealing with a single object, so it is also bringing in the single object mix-in

21:15

to give you a model form dealing with a single object. And then at the end of the day, having handled the f got the object, handled the form, dealt with it appropriately, we need to give a response back to someone, we have a single object template response mix in. Which says, given that I know I've got a single object in my context and it's been handled in an appropriate way, render it, render that single object into a template. Okay, and again the single object template response mix-in is an extension of template response mix-in, which is the generic I need to return a template. The single object template response mix-in is I need to return a template that deals with a single object So there we go, simple. Nine classes to go and get your nine nine mix-ins to get you a simple view that allows you to do updating a single object with a form.

22:02

What's the problem? Okay, so but there's this is this is why it's so complicated. Let's say we want to move from being an edit view To being a create view. What's the difference in the processing? Well, fundamentally it's exactly the same, except that instead of creating or getting an existing object, we need to be able to instantiate a dummy object and then make sure that dummy object gets Well here is the new object we want to create. So all we really want to modify is the bit right at the beginning. The get object needs to turn into instantiate an object. So all we need to do is change the base update view to a base create view that knows how to create objects rather than getting an existing one. Okay, so the most of the code is exactly the same. We just have to use one slightly different base class which knows how to instantiate rather than get than retrieving from a database.

22:53

Well, okay, what about instead of uh creating, we've got this create view, instead of responding as a template, we want to respond uh with JSON content. We want to build an API. Well, okay. The creation process, we're still gonna be dealing with a form, we're still gonna be dealing with instantiating an object. All that stuff is the same. The only thing that's different is down the bottom, instead of rendering to a template, we want to render to a JSON response. So you can switch out the single template uh single object template response mix in with a single object JSON response mix in and all of a sudden it starts generating JSON content. And everything else can get reused. What does it look like when it actually gets deployed? Okay, well, this is your generic function-based view. You're asking for update object, you pass in the class we want to configure, it's the book,

23:39

and we give it a URL. Effectively, this is all you're doing. You're saying instead of passing in the function update view, you get the class update view and you say I want to use this as a view for the book model. Okay, and this is the bit I was talking about. The the that is not necessarily the obvious way to use a class. There's a reason it needs to be done this way though. So um dig into the the that wiki page I gave you if you want to um if you want to see the reason why That is doing it in one line. If you do, however, want to uh you know bring it out as a class in your own, that's essentially just shorthand for saying I've got this class called a book update view. it extends generic update view and it's configured to use a particular model as a book.

24:24

And then you just say book update view. as view and there you go. You can deploy that you know multiple times in your URL structure if you wanted to. Okay, so but other than using up more lines of code, um why would you want to pull this out as a separate class? Well the reason Is it then you can extend it yourself? So subclassing lets you dig into the internals and overwrite your own pieces of behavior if you want to. So remember remember back when we said uh you couldn't inject logic into the process to say you know if this form is successful but before we save it we want to do some extra processing. Well, okay, all you've got to do is say here's form valid By default, form valid will save the form. But we want to do some processing first, self. object. process, and then just do whatever the base class wanted us to do. Go off and save the form. Okay, so you can easily inject logic in and all you need to do is override a single method.

25:13

You need to know that that's the method you need to need to override, but you override one method, inject the logic into the flow where you want, and off you go Okay, well what about something more complex? Um let's have a look at something like permissions. Okay, uh you can use mix-ins to define behavior and then inject that behavior into any class you want. So let's say we've got a really, really complex permission scheme here based upon random number generation. You want any attempt to access a single object to be checked against a dice roll, and if you pass the dice roll then you're allowed to see the object Fantastic. All you've got to do is override the way that you get that single object. By default, the implementation of getObject will go and get an object based upon the arguments that have been passed into the view. Overwrite it, put some extra logic in there to say

25:58

if the random number exceeds whatever threshold, uh then you're allowed to see it. Otherwise, return a 404, no you're not. Or any other 404 if you want to do it, any other HTTP response if you want to do it want. And then all you've got to do is say, okay, well there's my mix in, it defines getObject. You can drop that into any view, update view, create view, whatever. You've got a reusable block of random-based permissions logic. uh to to inject into any view that you want to do you want to handle that with. Now as a side note the form valid method that I had on the previous slide you could have done that as a mix-in as well. It's just a question of how reusable a piece of logic is A specific post-save behavior probably doesn't have a lot of reuse potential, so an override in a subclass makes a little bit of sense. Permission checking is something that's likely to you're likely to have the same permission scheme uh in existence for all objects.

26:48

So pull that into a mix-in, reuse that mix-in over and over again. So at the end of the day, remember ravioli tastes good. There's a reason the Italians keep making it. Ravioli allows or ravioli code in this context. Let allows us to reuse core logic over and over again. You only need to define once how do I get a single object? And then you can reuse that logic everywhere you've got a view that deals with a single object It's extremely flexible for inserting new logic. It's easy to add your own mix-ins. But unfortunately, you need to grok all the pieces to be able to know where you've got to go. So, like I said, the technical term for the documentation state at time of delivery was bad. It got all the details 100%

27:35

correct, but completely missed the big picture Uh it's kind of like the the joke about the uh the person sailing by in a uh in a hot air balloon goes past the uh the the tall building, yells out, say, excuse me, can you uh so can you tell me where where am I? And the guy says, You're in a hot air balloon a hundred meters off the ground Technically, 100% correct. Utterly useless information. So yeah, the documentation mixed the big missed the big picture. You still also need to make some framework decisions. We have kind of not made at a project level a decision about whether we should be using class decorators. So take something like a login required decorator, a login required mechanism. Easy to do in a class but on a function-based view, you just wrap it with a decorator that says login required. Do you use a mix-in?

28:20

Do you use a class decorator? Do you use a method decorator on the class? There are multiple ways to skin that cat. And as a result, there's now kind of multiple ways to do it and no officially blessed right way as far as Django the project and our documentation is concerned. So at one level as a core team, we kind of need to bite the bullet, pick one, and then encourage everyone to kind of run with it. Okay, so where do we go from here? Documentation can always improve. There are some high-level decisions that we probably need to make and then integrate that into documentation. We can extend what is currently there. Like I said, when we landed in 1. 3, the generic class-based views, we very deliberately drew the line at replace what is there.

29:06

We had a create view, we had an update view, we had uh we had that sort of basic set. Let's just replace what's there. We know there are an infinite panoply of things we could build, let's sit to this, deliver that, and then we'll move on from there. Now that leaves lots of new territory. There are lots of additional class-based generic views that could be built. You know, there there wasn't a function-based generic view for handling form sets. So there isn't a class-based generic view for handling form sets either. It's an obvious addition. Or object plus inline form zen. This that sort of thing is it isn't an obvious um it's an obvious construct to have because we do it in admin So there is room to expand and add those generic class-based views into Django and officially bless them.

29:53

So why haven't we added them in the intervenient years? Well, first off, I need to ask the question, have we solved the wrong problem here? Um if we go back 30 odd slides. Uh I put up this list of uh features of generic views. And we need to be able to display a template. We need to be able to display a list uh a single object, a list of objects. a form, we need to be able to create, update, delete objects. We need to be able to display a date archive. These were all very, very common problems in 2005. You know, we were coming from a world where we had great big Java based websites that we all wanted to kill ourselves we had to deal with them. And we've replaced it with this lightweight, fresh framework. Let's just build these functions really, really easily because we were solving the problems we had in 2005.

30:39

Modern websites have different problems, or at least much more involved answers to the same problems. A you know a a genuine hey look this is a this is a schmick modern website has multiple forms and form sets per page. It has continuous scrolling rather than pagination-based uh uh views of the world. It will almost inevitably be dealing with Ajax at some level, including things like in-place editing. and in uh on on s on on page live form validation of content. Um you've got have things like Pjax for navigation, so like the GitHub thing where you the page header is always there and you navigate and the URL changes but you're only really reloading part of the page's content. You'll have the concept of having multiple actions per page.

31:25

You don't go to the create page to create. You'll go to a page and you'll have a whole bunch of different actions you can do that you want to be able to interact with each one of them. And then you've got rich JavaScript driven user interfaces where you've got the there yes, there is back end data that's being provided to you, but what you've got on the front end is a rich API-driven user interface. The emergence of things like React and Angular and Ember are kind of a this is what the tools we're using to build these front-end front-end tools. And Django has kind of historically said, no, we're just server-side. Um we don't deal with the front end. But as a result, we kind of haven't adapted well to these problems that exist and interacting with these with the front end problems. So In particular, the API-driven user interface is something that I kind of want to harp on.

32:11

As an audience, I think we can take it as a given that good websites, you know, good Good software products that we're delivering these days have APIs. They documented their version full first-class citizens of the ecosystem we're trying to trying to develop. And like I said, by delivering this as sort of part of the website experience, you actually genuinely build an ecosystem. It enables other people to remix and reuse the functionality of this website in ways that you as the person who just delivered a user interface uh possibly haven't envisaged or or or or conceived of. And if you think about it, is there any reason that an appropriately authenticated automated process shouldn't be able to do everything that an appropriately authenticated actual human can do. The easiest way to make this happen is to make the user interface and the API have functional parity

32:59

And at the end point, really, a user interface is just a way to allow Meat to invoke an API and then visualize the response of that in a way that Meat can understand. So why not make the API that the center of what we're working on here? Completely aside from the sort of force multiplier benefits, there's a simple engineering benefit because APIs are easily tested. And if the user interface is just invoking an API, then you've got a good conceptual separation between what it is we're actually trying to achieve that we can then test ser test seriously, and we've got a user interface that needs to invoke that user that API, which we can test against You've got good separation there between the presentation and between the actual the functionality you're trying to drive. It gives you a testing boundary. API-driven user interfaces are

33:45

really easy to generate in Django. We've got APIs are easy to generate, we've got Django REST framework, we've got TastyPy and others that are out there. But what we're missing are the views, the the building the user interfaces. We still need to be able to describe navigation and composition of various pieces of functionality on a page. And all this, I would argue, requires that we have a strong base framework to work with. And my argument is that class-based views provide that framework. Not class-based generic views. I I I'm very strong about pointing that out. The class-based generic views are a great solution to the problem we had in 2005. I don't think the generic views we've gotten today are a great solution for today's problems. But that doesn't mean that class-based views can't be the basis for what we do.

34:31

Why do I say that with confidence? Well, to a certain level, it's already been done. DjangoContrib. admin is a class-based view. It doesn't eat Django's own dog food But it is class-based in that sense. You've got this object, an admin object that you deploy into a URL structure that then knows how to do other more interesting things. Interestingly, it's implemented using a single call method, which is one of the approaches that the class-based views that we've delivered as part of 1. 3 rejected because there is actually some security implications in there. Again, dig into the documentation if you want to find about that, or come and ask me and I can um I can sort of delve a little bit into the details for that. So we we've got a proof that we can build a class-based

35:18

complex user interface. Use class-based mechanisms to allow people to have extension points to add functionality to a complex user interface. And I think this is something that's worth exploring a little bit more. Django, this this however doesn't need necessarily to be part of Django's core. Django's admin was distributed as part of core, but that's kind of a conceit of the packaging infrastructure that existed in 2005 , which is to say that there wasn't packaging infrastructure in 2005. These days, pip install Django Foo works and everyone knows it works and everyone just has their requirements file and deploys websites to rely on it. We could conceptually pull Django admin out into a completely separate package. and then just have it automatically be installed when you install Django. It's easy to do that kind of thing now with the state of setup tools being where it is.

36:07

So If we're going to genuinely replace admin or use admin as a testing ground for what we can do long term instead of class-based structures. We can do this completely outside of core, maybe with a long-term image of bringing it in as a core managed project or something, but exploring this idea of can we build rich user interfaces at a code level, class-based, with easy extension points and what have you, to support all of this API over here, user interface control over here. connected over over the API layer. This is something we can test bid outside of Django, outside of Core, and then possibly bring in interesting parts of it into Core as a long-term thing. So, okay, there's sort of a call to action here.

36:52

If you are out in the field and you are discussing class-based views and class-based generic views, please be clear about which of those you are actually referring to. If you're criticizing class-based views, are you criticizing class-based views or are you clearly criticizing class-based generic views? I will heartily endorse anyone criticizing class-based generic views. They have um they have a multitude of problems. Criticizing class-based views, however, you'll have a lot more struggle getting that argument across with me because I think they're a much much stronger framework that people give it credit for. Documentation can still be improved. So if you are interested in those class-based generic views, or in just in class-based views for that matter, there is a lot of work we could do to improve that documentation. And we've got sprints coming up this weekend. So if you feel like contributing, that would certainly be a helpful way to contribute. If you are interested in extending what is there, there are tickets, things like uh 18830 is a is a ticket that I logged a while back, but an idea called Form Collection.

37:44

ways of getting richer functionality into class-based generic views without necessarily adding a whole new farm of uh of class-based generic view mix-ins and what have you. But also experiment, experiment with APIs, this sort of broad idea of building rich user interfaces at a functional level to say Django's admin is really just a uh uh deeply introspective, but it's really just a user interface to say here's a list of things we can get at, and here is a way of displaying an object and having multiple pieces of functionality with filtering and uh you know in-place editing and in in uh uh in-place uh form submission This is an idea we can play around with. I'm not saying I have any solutions. I'm certainly interested in talking about it if anybody wants to talk about it. I'm interested to talk about many things at great length.

38:29

So Play around. We don't have to s to live with just because of the tools we've got are the tools we've got. Django needs to continue to evolve or we run the risk of being uh taken over by projects like Meteor and Node. js and Please, I don't want to write JavaScript for the rest of my life. But the only way we're gonna be able to compete with Meteor's demo of hey look a live chat server in in in twent in ten minutes is to have a compelling counterexample that we can build things much more complex than that. in 10 minutes. So from that, if you've got questions, I'm happy to answer questions. And hopefully I've seeded some ideas into the community and said a few things straight about the state of class-based views.

Questions this talk answers

Why were Django generic views introduced?

Generic views were introduced to avoid repeating common view code. They provide reusable patterns for displaying templates or objects, handling lists and forms, performing create/update/delete operations, and showing date-based archives.

Discussed at 1:52

What problems do class-based generic views solve compared with function-based generic views?

Function-based generic views are limited by the configuration arguments and callbacks they expose, making it difficult to inject custom form logic, pass extra arguments, or add permissions cleanly. Class-based generic views address this through subclassing, method overrides, and mixins.

Discussed at 6:45

What are Django class-based views?

A class-based view is a class-based analogue of a view function that dispatches HTTP verbs such as GET and POST to corresponding methods. The base class also provides features such as OPTIONS handling, basic HEAD handling, and HTTP 405 responses for unsupported methods.

Discussed at 12:39

What does the “ravioli code” criticism of Django class-based views mean?

It means that the resulting views are convenient and reusable on the outside, but their internals are split across many classes and mixins. Using anything beyond a trivial case requires understanding that complex structure, which creates a steep learning curve.

Discussed at 18:03

How do you use a class-based view in Django URL patterns?

Use the class’s as_view() method when adding it to URL patterns, optionally configuring the view with a model or other attributes. You can also define a configured subclass and deploy that subclass with as_view().

Discussed at 23:39

How do you customize a Django class-based generic view?

Subclass the generic view and override the method corresponding to the behavior you need to change—for example, override form_valid() to run logic before saving. Reusable behavior, such as permission checks, can instead be packaged in a mixin and added to multiple views.

Discussed at 24:24

Are Django’s existing class-based generic views suitable for modern websites?

They are a good solution to the common server-rendered website problems of 2005, but they do not fully address modern needs such as multiple forms, AJAX, in-place editing, continuous scrolling, rich JavaScript interfaces, and API-driven applications. Russell argues that the more fundamental class-based view mechanism can still provide a foundation for solving those newer problems.

Discussed at 29:29

How could class-based views support modern Django user interfaces?

Class-based views could provide an extensible framework for composing user interfaces around APIs, while allowing reusable extension points for navigation and page functionality. Django’s class-based admin is presented as evidence that this approach can support complex interfaces, potentially with experimentation outside Django core first.

Discussed at 33:45

Note: We understand that names change, people change, and bodies change. We respect each individual's journey and privacy. If you have any concerns about a video or need us to remove content, please don't hesitate to contact us. We will handle your request with care and promptly address any issues.

More videos by Dr. Russell Keith-Magee

More videos from DjangoCon US