Class-based Views: Past, Present and Future
Keith-Magee, Russell
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.
 Okay, so for those who don't know me, my name is Russell Keith McGee. My day job is as the CTO and co-founder of TradesCloud, we're a cloud provider of 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 since January of 2006. I've been president of the Django Software Foundation since 2010. The DSF, as I said in my introduction this morning, is the IP Legal and Fundraising Armour of the Django project. So if you are interested, if you represent a company that has money, I'd like to talk to you because we need your money. But 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.3, but have not been, how should I put this, greeted with universal enthusiasm. So the reason I'm giving this talk, and this is a redo of a talk that I gave about a month ago at DjangoCon Australia, 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 that sort of the fundamentals of those discussions sometimes get based on. So if we're talking big picture, to explain why it is what we've got, what we've got, it helps to know where we've come from. 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 mechanism for building websites. Views are there for displaying content. There are lots of common patterns in views that we all like being dry. We like not repeating ourselves. So Django had this concept 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, class-based views, sorry, generic views first landed in Django's code base. And they were added because we had these common problems we wanted to solve. We wanted to avoid 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 a list of articles by date is a common problem that people were trying to solve. Those generic views are then wrapped up in sort of a... They have a couple of key features. Each view is a function and you configure that function by passing in arguments to control how you want that function to operate. So, for example, a common... This is sort of 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 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, how we provided all the 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 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 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 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, I think your base idea of an update object, we're going to replace it with this idea of updating an object and configure it to say this update object was specifically we're going to be updating books. So we're going to 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 post 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 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 the 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 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 instantiate a different form, instead of a generic model form that's being created, you want to have a very specific form being 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, something really simple like the request. Well, no, unless you can put it into a decorator or something like that, 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 can 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're passing the function that you want to operate in this particular part of the function and get all completely metter and deep like that. But at some point, what you basically end up doing is poorly implementing half of the features of class inheritance. 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 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... Well, 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 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, 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 a collection of objects. And that's essentially what we did for Django 1.3. Now, it turns out that although that's the high-level architectural goal, 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. Jacob Kaplan-Moss tried. Joseph Cochran's tried. Ben Fershmann 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 of those gentlemen 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 has been a lot of confusion about what class-based views actually are and what they should be or can be used for. There's a lot of confusion because there's two very distinct purposes in the code base around this one commit that was the feature that got landed in 1.3. 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. It's implemented with class-based views. I implemented the way they are for a very specific reason but that reason leads to non-obvious complexity that if you come to the idea for the first time, you think, well, why is it being done this way? Isn't this other way? Wouldn't it have made a lot more sense and been a lot more obvious? There's a reason why it's not like that. The third criticism that sort of has been leveled is something that's labeled ravioli code. I'll come back to exactly what this means. It's a legitimate criticism that is a legitimate architectural criticism and I'll come sort of an explanation of what that means in a minute. The worst problem of all was that the documentation at the time it landed in 1.3 was 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 it was not good documentation by any stretch of the imagination. If anything else, it's an abject lesson in the person who writes the code, shouldn't be the person who documents it because it was obvious to me. Why is it not obvious to you guys? Since I landed my original documentation, Mark Tamlin, Danny Greenfield and a number of others have made some really big contributions and really big improvements in that area. That situation at least has got a little bit better. The rest of them still exist. Let's go through them and try to 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 embedded in it as part of the implementation, method-based HTTP verb dispatch and that's it. 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. For example, we go back to our... This is essentially a class-based view. 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 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 get a whole bunch of interesting features for free. You could get automated options handling. For those of you that are familiar with HTTP or the internals of HTTP, the HTTP verb is sort of the request, the 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 kind 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 get. You also get 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? 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. You can just do the get request and then don't return the body content. And you can get that for free. You also automatically get HTTP405 on any verb that isn't supported. So HTTP405 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 HTTP405. 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. So 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 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, trying to invent class-based views by passing in callable, or passing in more keyword arguments to the configuration of the function. The catch is that by addressing the limitations of the approach, 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 views work in order to be able to do anything with them. That's the ravioli criticism, essentially, and again, I'll come back to exactly what that means and 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 can't be done the obvious way. 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 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 has a complete teardown of the history of the development of class-based views. If you go to that URL, what you will find is these are the 10 options we looked at. Here are the pros and cons of every one of them, and this is why we chose this one. So here is the design decision behind why we've got what we've got. So if you're interested, it's certainly worth a read, and you can sort of find the back story for why you have to do as view. Okay. Problem three, ravioli. This phrase came into being because 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, sort of as a product, is delicious and tasty and looks lovely, unless it's a square packet or triangular packet that's sitting in your plate. The internals, however, don't ask too many questions. 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 generic class-based views have a very, very complex internal structure. There are lots of entry points. There are all sorts of subclasses and mixings that come together to give you the final end product that is an update view. Unfortunately, in order to use them at all for anything other than the most trivial case, you need to understand all of it. 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 pseudo-code 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 is 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 a generic class-based view? That's the collection of mixings 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. Yeah, so 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. 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 deferred to whatever it is the rest of this form of 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 and we need to 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 to give you a model form dealing with a single object. And then at the end of the day, having handled the, 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 mix ins to get you a simple view that allows you to do updating a single object with a form. What's the problem? Okay. So, but 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 stable. 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 objects 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 retrieving from a database. Okay. What about instead of creating, we've got this create view, instead of responding as a template, we want to respond with JSON content. We want to build an API. Well, okay. The creation process is still going to be dealing with a form. We're still going to 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, no, a single object template response mix-in with a single object JSON response mix-in and all of a sudden start 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 objects. You pass in the class. We want to configure to the book 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. That is not necessarily the obvious way to use a class. There's a reason it needs to be done this way, though. So dig into that wiki page I gave you if you want to see the reason why. That is doing it in one line. If you do have a want to 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. And then you just say, book update view.asview and there you go. You can deploy that multiple times in your URL structure if you wanted to. Okay. So other than using up more lines of code, why would you want to bring this out as a separate class? Well, the reason is that 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 back when we said 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 overwrite a single method. You need to know that's the method you 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? Let's have a look at something like permissions. Okay. You can use mixins 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 the 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 get object 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, if the random number exceeds whatever threshold, 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. And then all you've got to do is say, okay, well, there's my mixin, it defines get object, you can drop that into any view, update view, create view, whatever. You've got a reusable block of random based permissions logic to inject into any view that you want to do it. 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 mixin as well. It's just a question of how reusable a piece of logic is in practice. 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, you likely to have the same permission scheme in existence for all objects. So pull that into a mixin, reuse that mixin 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, ravioli code in this context 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 mixins. 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% correct but completely missed the big picture. It's kind of like the joke about the person sailing by in a hot air balloon goes past the tall building, yells out, say, excuse me, so can you tell me where am I? And the guy says, you're in a hot air balloon 100 meters off the ground. Technically 100% correct, utterly useless information. So, yeah, the documentation 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 it takes something like a login required decorator, a login required mechanism. Easy to do on a function-based view, you just wrap it with a decorator that says login required. Do you use a mixin? 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, when we landed in 1.3, the generic class-based views, we very deliberately drew the line at replace what is there. We had a create view, we had an update view, 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. 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 set. That sort of thing, 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. So why haven't we added them in the intervening years? Well, first off, I need to ask the question, have we solved the wrong problem here? If we go back 30-odd slides, I put up this list of features of generic views and we need to be able to display a template. We need to be able to display 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. We were coming from a world where we had great big Java-based websites that we all wanted to kill ourselves if we had to deal with them. 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. Modern websites have different problems or at least much more involved answers to the same problems. A genuine, hey, look, this is a Schmitt modern website, has multiple forms and form sets per page. It has continuous scrolling rather than pagination-based views of the world. It will almost inevitably be dealing with Ajax at some level, including things like in-place editing and on-page live form validation of content. You've got to have things like PJAX for navigation, so like the GitHub thing where 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. 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. Then you've got rich JavaScript-driven user interfaces where you've got, 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... The tools we're using to build these front-end tools and Django has kind of historically said, no, we're just server-side. We don't deal with the front-end. As a result, we kind of haven't adapted well to these problems that exist and interacting with the front-end problems. In particular, the API-driven user interface is something that I kind of want to harp on. As an audience, I think we can take it as a given that good websites and good software products that we're delivering these days have APIs. They're documented. They're versioned, full first-class citizens of the ecosystem we're trying to develop. Like I said, by delivering this as 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 possibly haven't envisaged or conceived of. 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. 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. Why not make the API that the center we're working on here? Completely aside from the force multiplier benefits, there's a simple engineering benefit because APIs are easily tested. 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 seriously. We've got a user interface that needs to invoke that API, which we can test against. You've got good separation there between the presentation and between the functionality you're trying to drive. It gives you a testing boundary. API-driven user interfaces are really easy to generate in Django. APIs are easy to generate. We've got Django REST framework. We've got TastyPi and others that are out there. What we're missing are the views, the building the user interfaces. We still need to be able to describe navigation and composition of various pieces of functionality on a page. All of this, I would argue, requires that we have a strong base framework to work with. My argument is that class-based views provide that framework, not class-based generic views. I'm very strong at 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 got 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. Why do I say that with confidence? Well, to a certain level, it's already been done. Django Contrib.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 sort of delve a little bit into the details for that. So we got a proof that we can build a class-based 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, 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 filed and deploys websites to relies 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. 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 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, easy extension points and what have you to support all of this API over here, user interface control over here, connected over the API layer. This is something we can test bit 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. 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 criticizing class-based generic views? I will heartily endorse anyone criticizing class-based generic views. 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 there are much, much stronger framework that people give 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 18.8.30 is a ticket that I've logged a while back, but an idea called form collection, ways of getting richer functionality into class-based generic views without necessarily adding a whole new farm 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 at Django's admin is really just a deeply introspective, but it's really just a user interface to say here is 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 in-place editing and in-place 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, great length. So play around. We don't have to live with it just because of the tools we've got or the tools we've got. Django needs to continue to evolve or we run the risk of being 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 going to be able to compete with Meteor's demo of hey, look a live chat server in 10 minutes is to have a compelling counter example 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 Classbox Views. Thank you.
 Okay, so for those who don't know me, my name is Russell Keith McGee. My day job is as the  CTO and co-founder of TradesCloud, we're a cloud provider of 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  since January of 2006. I've been president of the Django Software Foundation since 2010.  The DSF, as I said in my introduction this morning, is the IP Legal and Fundraising Armour  of the Django project. So if you are interested, if you represent a company that has money,  I'd like to talk to you because we need your money. But 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.3, but have not been, how should I put this, greeted with universal enthusiasm. So the  reason I'm giving this talk, and this is a redo of a talk that I gave about a month ago  at DjangoCon Australia, 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 that sort of the fundamentals of those discussions sometimes get based on.  So if we're talking big picture, to explain why it is what we've got, what we've got,  it helps to know where we've come from. 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 mechanism  for building websites. Views are there for displaying content. There are lots of common  patterns in views that we all like being dry. We like not repeating ourselves. So Django  had this concept 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, class-based views, sorry, generic  views first landed in Django's code base. And they were added because we had these common  problems we wanted to solve. We wanted to avoid 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 a list  of articles by date is a common problem that people were trying to solve. Those generic  views are then wrapped up in sort of a... They have a couple of key features. Each view is  a function and you configure that function by passing in arguments to control how you  want that function to operate. So, for example, a common... This is sort of 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 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, how we provided all the 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 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 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 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, I think your  base idea of an update object, we're going to replace it with this idea of updating an  object and configure it to say this update object was specifically we're going to be  updating books. So we're going to 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 post 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  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 the 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 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 instantiate a different form,  instead of a generic model form that's being created, you want to have a very specific  form being 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,  something really simple like the request. Well, no, unless you can put it into a decorator  or something like that, 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 can 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're passing the function  that you want to operate in this particular part of the function and get all completely  metter and deep like that. But at some point, what you basically end up doing is poorly  implementing half of the features of class inheritance. 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  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... Well, 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 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, 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 a collection of objects. And that's essentially what we did for Django  1.3. Now, it turns out that although that's the high-level architectural goal, 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. Jacob  Kaplan-Moss tried. Joseph Cochran's tried. Ben Fershmann 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 of those gentlemen 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 has been a lot of confusion about what class-based views actually are and what  they should be or can be used for. There's a lot of confusion because there's two very  distinct purposes in the code base around this one commit that was the feature that  got landed in 1.3. 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. It's implemented  with class-based views. I implemented the way they are for a very specific reason but  that reason leads to non-obvious complexity that if you come to the idea for the first  time, you think, well, why is it being done this way? Isn't this other way? Wouldn't it  have made a lot more sense and been a lot more obvious? There's a reason why it's not  like that. The third criticism that sort of has been leveled is something that's labeled  ravioli code. I'll come back to exactly what this means. It's a legitimate criticism that  is a legitimate architectural criticism and I'll come sort of an explanation of what that  means in a minute. The worst problem of all was that the documentation at the time it  landed in 1.3 was 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 it was not good documentation by any stretch  of the imagination. If anything else, it's an abject lesson in the person who writes  the code, shouldn't be the person who documents it because it was obvious to me. Why is it  not obvious to you guys? Since I landed my original documentation, Mark Tamlin, Danny  Greenfield and a number of others have made some really big contributions and really big  improvements in that area. That situation at least has got a little bit better. The rest  of them still exist. Let's go through them and try to 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 embedded  in it as part of the implementation, method-based HTTP verb dispatch and that's it. 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. For example, we go back to our... This  is essentially a class-based view. 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 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 get a whole bunch of interesting features for free. You could get automated  options handling. For those of you that are familiar with HTTP or the internals of HTTP,  the HTTP verb is sort of the request, the 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 kind 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 get. You also get 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? 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. You can just do the get request and then don't  return the body content. And you can get that for free. You also automatically get HTTP405  on any verb that isn't supported. So HTTP405 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 HTTP405. 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. So 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 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,  trying to invent class-based views by passing in callable, or passing in more keyword arguments  to the configuration of the function. The catch is that by addressing the limitations  of the approach, 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 views  work in order to be able to do anything with them. That's the ravioli criticism, essentially,  and again, I'll come back to exactly what that means and 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 can't be done the obvious way. 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 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 has a complete teardown of the history  of the development of class-based views. If you go to that URL, what you will find is  these are the 10 options we looked at. Here are the pros and cons of every one of them,  and this is why we chose this one. So here is the design decision behind why we've got  what we've got. So if you're interested, it's certainly worth a read, and you can sort  of find the back story for why you have to do as view.  Okay. Problem three, ravioli. This phrase came into being because 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, sort of as a product, is delicious and tasty and  looks lovely, unless it's a square packet or triangular packet that's sitting in your  plate. The internals, however, don't ask too many questions. 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 generic class-based views have a very,  very complex internal structure. There are lots of entry points. There are all sorts  of subclasses and mixings that come together to give you the final end product that is  an update view. Unfortunately, in order to use them at all for anything other than the  most trivial case, you need to understand all of it. 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 pseudo-code 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 is 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 a generic class-based  view? That's the collection of mixings 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. Yeah, so 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.  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 deferred to whatever it is the rest  of this form of 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 and we need to 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 to give you a model form dealing with  a single object. And then at the end of the day, having handled the, 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 mix ins to get you a simple view that allows you to do updating a single object  with a form. What's the problem? Okay. So, but 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 stable. 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 objects 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 retrieving from a database. Okay.  What about instead of creating, we've got this create view, instead of responding as  a template, we want to respond with JSON content. We want to build an API. Well, okay. The creation  process is still going to be dealing with a form. We're still going to 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, no, a single object template response mix-in  with a single object JSON response mix-in and all of a sudden start 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 objects. You  pass in the class. We want to configure to the book 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. That is not necessarily the obvious way to  use a class. There's a reason it needs to be done this way, though. So dig into that  wiki page I gave you if you want to see the reason why. That is doing it in one line.  If you do have a want to 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. And then you just say, book  update view.asview and there you go. You can deploy that multiple times in your URL structure  if you wanted to. Okay. So other than using up more lines of code, why would you want  to bring this out as a separate class? Well, the reason is that 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 back when we said 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 overwrite a single method.  You need to know that's the method you 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? Let's have a look at something like permissions. Okay. You can use mixins  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 the 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 get object 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, if the random number exceeds  whatever threshold, 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. And  then all you've got to do is say, okay, well, there's my mixin, it defines get object, you  can drop that into any view, update view, create view, whatever. You've got a reusable  block of random based permissions logic to inject into any view that you want to do it.  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 mixin as well. It's just a question of  how reusable a piece of logic is in practice. 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, you likely to have the same permission scheme  in existence for all objects. So pull that into a mixin, reuse that mixin 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, ravioli code in this context 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 mixins. 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% correct but completely missed the big picture. It's kind  of like the joke about the person sailing by in a hot air balloon goes past the tall  building, yells out, say, excuse me, so can you tell me where am I? And the guy says,  you're in a hot air balloon 100 meters off the ground. Technically 100% correct, utterly  useless information. So, yeah, the documentation 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 it takes something like a login required  decorator, a login required mechanism. Easy to do on a function-based view, you just wrap  it with a decorator that says login required. Do you use a mixin? 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, when we landed in 1.3, the generic class-based views, we very  deliberately drew the line at replace what is there. We had a create view, we had an  update view, 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. 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 set. That sort of thing, 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. So why haven't we added them in  the intervening years? Well, first off, I need to ask the question, have we solved the  wrong problem here? If we go back 30-odd slides, I put up this list of features of generic  views and we need to be able to display a template. We need to be able to display 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. We were coming from a world where we had great big Java-based websites that  we all wanted to kill ourselves if we had to deal with them. 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. Modern websites have different problems  or at least much more involved answers to the same problems. A genuine, hey, look, this  is a Schmitt modern website, has multiple forms and form sets per page. It has continuous  scrolling rather than pagination-based views of the world. It will almost inevitably be  dealing with Ajax at some level, including things like in-place editing and on-page  live form validation of content. You've got to have things like PJAX for navigation,  so like the GitHub thing where 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. 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. Then you've got rich JavaScript-driven user  interfaces where you've got, 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... The tools we're using to  build these front-end tools and Django has kind of historically said, no, we're just  server-side. We don't deal with the front-end. As a result, we kind of haven't adapted well  to these problems that exist and interacting with the front-end problems.  In particular, the API-driven user interface is something that I kind of want to harp on.  As an audience, I think we can take it as a given that good websites and good software  products that we're delivering these days have APIs. They're documented. They're versioned,  full first-class citizens of the ecosystem we're trying to develop. Like I said, by delivering  this as 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 possibly haven't envisaged or conceived  of.  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. 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. Why not make the API that the center we're working on here? Completely aside from  the force multiplier benefits, there's a simple engineering benefit because APIs are easily  tested. 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 seriously.  We've got a user interface that needs to invoke that API, which we can test against. You've  got good separation there between the presentation and between the functionality you're trying  to drive. It gives you a testing boundary.  API-driven user interfaces are really easy to generate in Django. APIs are easy to generate.  We've got Django REST framework. We've got TastyPi and others that are out there. What  we're missing are the views, the building the user interfaces. We still need to be  able to describe navigation and composition of various pieces of functionality on a page.  All of this, I would argue, requires that we have a strong base framework to work with.  My argument is that class-based views provide that framework, not class-based generic views.  I'm very strong at 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 got 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.  Why do I say that with confidence? Well, to a certain level, it's already been done.  Django Contrib.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 sort of delve a little bit into the details for that.  So we got a proof that we can build a class-based 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, 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 filed  and deploys websites to relies 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. 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 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,  easy extension points and what have you to support all of this API over here, user interface  control over here, connected over the API layer. This is something we can test bit 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. 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 criticizing class-based generic  views? I will heartily endorse anyone criticizing class-based generic views. 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 there are much, much stronger framework that  people give 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  18.8.30 is a ticket that I've logged a while back, but an idea called form collection,  ways of getting richer functionality into class-based generic views without necessarily  adding a whole new farm 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 at Django's admin is really just a deeply introspective, but it's really  just a user interface to say here is 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  in-place editing and in-place 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, great length. So play  around. We don't have to live with it just because of the tools we've got or the tools  we've got. Django needs to continue to evolve or we run the risk of being 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 going to be able to compete with Meteor's demo of  hey, look a live chat server in 10 minutes is to have a compelling counter example 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 Classbox Views.  Thank you.
DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_0_143.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_144_419.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_420_503.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_504_875.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_876_1487.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_1488_1583.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_1584_1835.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_1836_2231.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_2232_4391.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_4392_9395.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_9396_11915.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_11916_12947.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_12948_13163.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_13164_13283.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_13284_13439.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_13440_13703.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_13704_13775.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_13776_14159.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_14160_14279.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_14280_14363.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_14364_15131.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_15132_24875.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_24876_28739.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_28740_35543.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_35544_43451.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_43452_43499.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_43500_43559.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_43560_43871.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_43872_44435.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_44436_44531.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_44532_44831.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_44832_44927.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_44928_45971.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_45972_46283.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_46284_50555.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_50556_51863.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_51864_53279.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_53280_58931.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_58932_59099.png DjangoCon_2014-_Class-based_Views__Past__Present_and_Future_8_x6qLfZjjs_59100_59388.png
Okay, so for those who don't know me, my name is Russell Keith McGee. My day job is as the CTO and co-founder of TradesCloud, we're a cloud provider of 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 since January of 2006. I've been president of the Django Software Foundation since 2010. The DSF, as I said in my introduction this morning, is the IP Legal and Fundraising Armour of the Django project. So if you are interested, if you represent a company that has money, I'd like to talk to you because we need your money. But 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.3, but have not been, how should I put this, greeted with universal enthusiasm. So the reason I'm giving this talk, and this is a redo of a talk that I gave about a month ago at DjangoCon Australia, 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 that sort of the fundamentals of those discussions sometimes get based on. So if we're talking big picture, to explain why it is what we've got, what we've got, it helps to know where we've come from. 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 mechanism for building websites. Views are there for displaying content. There are lots of common patterns in views that we all like being dry. We like not repeating ourselves. So Django had this concept 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, class-based views, sorry, generic views first landed in Django's code base. And they were added because we had these common problems we wanted to solve. We wanted to avoid 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 a list of articles by date is a common problem that people were trying to solve. Those generic views are then wrapped up in sort of a... They have a couple of key features. Each view is a function and you configure that function by passing in arguments to control how you want that function to operate. So, for example, a common... This is sort of 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 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, how we provided all the 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 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 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 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, I think your base idea of an update object, we're going to replace it with this idea of updating an object and configure it to say this update object was specifically we're going to be updating books. So we're going to 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 post 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 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 the 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 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 instantiate a different form, instead of a generic model form that's being created, you want to have a very specific form being 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, something really simple like the request. Well, no, unless you can put it into a decorator or something like that, 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 can 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're passing the function that you want to operate in this particular part of the function and get all completely metter and deep like that. But at some point, what you basically end up doing is poorly implementing half of the features of class inheritance. 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 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... Well, 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 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, 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 a collection of objects. And that's essentially what we did for Django 1.3. Now, it turns out that although that's the high-level architectural goal, 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. Jacob Kaplan-Moss tried. Joseph Cochran's tried. Ben Fershmann 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 of those gentlemen 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 has been a lot of confusion about what class-based views actually are and what they should be or can be used for. There's a lot of confusion because there's two very distinct purposes in the code base around this one commit that was the feature that got landed in 1.3. 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. It's implemented with class-based views. I implemented the way they are for a very specific reason but that reason leads to non-obvious complexity that if you come to the idea for the first time, you think, well, why is it being done this way? Isn't this other way? Wouldn't it have made a lot more sense and been a lot more obvious? There's a reason why it's not like that. The third criticism that sort of has been leveled is something that's labeled ravioli code. I'll come back to exactly what this means. It's a legitimate criticism that is a legitimate architectural criticism and I'll come sort of an explanation of what that means in a minute. The worst problem of all was that the documentation at the time it landed in 1.3 was 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 it was not good documentation by any stretch of the imagination. If anything else, it's an abject lesson in the person who writes the code, shouldn't be the person who documents it because it was obvious to me. Why is it not obvious to you guys? Since I landed my original documentation, Mark Tamlin, Danny Greenfield and a number of others have made some really big contributions and really big improvements in that area. That situation at least has got a little bit better. The rest of them still exist. Let's go through them and try to 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 embedded in it as part of the implementation, method-based HTTP verb dispatch and that's it. 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. For example, we go back to our... This is essentially a class-based view. 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 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 get a whole bunch of interesting features for free. You could get automated options handling. For those of you that are familiar with HTTP or the internals of HTTP, the HTTP verb is sort of the request, the 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 kind 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 get. You also get 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? 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. You can just do the get request and then don't return the body content. And you can get that for free. You also automatically get HTTP405 on any verb that isn't supported. So HTTP405 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 HTTP405. 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. So 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 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, trying to invent class-based views by passing in callable, or passing in more keyword arguments to the configuration of the function. The catch is that by addressing the limitations of the approach, 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 views work in order to be able to do anything with them. That's the ravioli criticism, essentially, and again, I'll come back to exactly what that means and 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 can't be done the obvious way. 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 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 has a complete teardown of the history of the development of class-based views. If you go to that URL, what you will find is these are the 10 options we looked at. Here are the pros and cons of every one of them, and this is why we chose this one. So here is the design decision behind why we've got what we've got. So if you're interested, it's certainly worth a read, and you can sort of find the back story for why you have to do as view. Okay. Problem three, ravioli. This phrase came into being because 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, sort of as a product, is delicious and tasty and looks lovely, unless it's a square packet or triangular packet that's sitting in your plate. The internals, however, don't ask too many questions. 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 generic class-based views have a very, very complex internal structure. There are lots of entry points. There are all sorts of subclasses and mixings that come together to give you the final end product that is an update view. Unfortunately, in order to use them at all for anything other than the most trivial case, you need to understand all of it. 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 pseudo-code 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 is 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 a generic class-based view? That's the collection of mixings 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. Yeah, so 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. 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 deferred to whatever it is the rest of this form of 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 and we need to 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 to give you a model form dealing with a single object. And then at the end of the day, having handled the, 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 mix ins to get you a simple view that allows you to do updating a single object with a form. What's the problem? Okay. So, but 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 stable. 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 objects 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 retrieving from a database. Okay. What about instead of creating, we've got this create view, instead of responding as a template, we want to respond with JSON content. We want to build an API. Well, okay. The creation process is still going to be dealing with a form. We're still going to 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, no, a single object template response mix-in with a single object JSON response mix-in and all of a sudden start 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 objects. You pass in the class. We want to configure to the book 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. That is not necessarily the obvious way to use a class. There's a reason it needs to be done this way, though. So dig into that wiki page I gave you if you want to see the reason why. That is doing it in one line. If you do have a want to 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. And then you just say, book update view.asview and there you go. You can deploy that multiple times in your URL structure if you wanted to. Okay. So other than using up more lines of code, why would you want to bring this out as a separate class? Well, the reason is that 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 back when we said 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 overwrite a single method. You need to know that's the method you 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? Let's have a look at something like permissions. Okay. You can use mixins 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 the 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 get object 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, if the random number exceeds whatever threshold, 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. And then all you've got to do is say, okay, well, there's my mixin, it defines get object, you can drop that into any view, update view, create view, whatever. You've got a reusable block of random based permissions logic to inject into any view that you want to do it. 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 mixin as well. It's just a question of how reusable a piece of logic is in practice. 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, you likely to have the same permission scheme in existence for all objects. So pull that into a mixin, reuse that mixin 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, ravioli code in this context 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 mixins. 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% correct but completely missed the big picture. It's kind of like the joke about the person sailing by in a hot air balloon goes past the tall building, yells out, say, excuse me, so can you tell me where am I? And the guy says, you're in a hot air balloon 100 meters off the ground. Technically 100% correct, utterly useless information. So, yeah, the documentation 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 it takes something like a login required decorator, a login required mechanism. Easy to do on a function-based view, you just wrap it with a decorator that says login required. Do you use a mixin? 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, when we landed in 1.3, the generic class-based views, we very deliberately drew the line at replace what is there. We had a create view, we had an update view, 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. 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 set. That sort of thing, 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. So why haven't we added them in the intervening years? Well, first off, I need to ask the question, have we solved the wrong problem here? If we go back 30-odd slides, I put up this list of features of generic views and we need to be able to display a template. We need to be able to display 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. We were coming from a world where we had great big Java-based websites that we all wanted to kill ourselves if we had to deal with them. 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. Modern websites have different problems or at least much more involved answers to the same problems. A genuine, hey, look, this is a Schmitt modern website, has multiple forms and form sets per page. It has continuous scrolling rather than pagination-based views of the world. It will almost inevitably be dealing with Ajax at some level, including things like in-place editing and on-page live form validation of content. You've got to have things like PJAX for navigation, so like the GitHub thing where 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. 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. Then you've got rich JavaScript-driven user interfaces where you've got, 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... The tools we're using to build these front-end tools and Django has kind of historically said, no, we're just server-side. We don't deal with the front-end. As a result, we kind of haven't adapted well to these problems that exist and interacting with the front-end problems. In particular, the API-driven user interface is something that I kind of want to harp on. As an audience, I think we can take it as a given that good websites and good software products that we're delivering these days have APIs. They're documented. They're versioned, full first-class citizens of the ecosystem we're trying to develop. Like I said, by delivering this as 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 possibly haven't envisaged or conceived of. 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. 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. Why not make the API that the center we're working on here? Completely aside from the force multiplier benefits, there's a simple engineering benefit because APIs are easily tested. 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 seriously. We've got a user interface that needs to invoke that API, which we can test against. You've got good separation there between the presentation and between the functionality you're trying to drive. It gives you a testing boundary. API-driven user interfaces are really easy to generate in Django. APIs are easy to generate. We've got Django REST framework. We've got TastyPi and others that are out there. What we're missing are the views, the building the user interfaces. We still need to be able to describe navigation and composition of various pieces of functionality on a page. All of this, I would argue, requires that we have a strong base framework to work with. My argument is that class-based views provide that framework, not class-based generic views. I'm very strong at 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 got 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. Why do I say that with confidence? Well, to a certain level, it's already been done. Django Contrib.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 sort of delve a little bit into the details for that. So we got a proof that we can build a class-based 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, 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 filed and deploys websites to relies 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. 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 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, easy extension points and what have you to support all of this API over here, user interface control over here, connected over the API layer. This is something we can test bit 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. 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 criticizing class-based generic views? I will heartily endorse anyone criticizing class-based generic views. 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 there are much, much stronger framework that people give 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 18.8.30 is a ticket that I've logged a while back, but an idea called form collection, ways of getting richer functionality into class-based generic views without necessarily adding a whole new farm 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 at Django's admin is really just a deeply introspective, but it's really just a user interface to say here is 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 in-place editing and in-place 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, great length. So play around. We don't have to live with it just because of the tools we've got or the tools we've got. Django needs to continue to evolve or we run the risk of being 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 going to be able to compete with Meteor's demo of hey, look a live chat server in 10 minutes is to have a compelling counter example 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 Classbox Views. Thank you.
Class-based views were introduced in Django 1.3, one commit that was the feature that got landed of the approach, the functional approach, is that the big picture. You still also need to make some