= Class-Based Views = The proposal to add a class-based framework for describing views (#6735) has been around for a while. It's been proposed for inclusion in Django 1.0, 1.1, and 1.2. This is a summary of the state of debate, with the intention of landing a patch for the 1.3 release. == The brief == At present, generic views are defined as methods. If you want to customize them, you have to pass in arguments to the view at time of use in urls.py. This is cumbersome: * urls.py rapidly becomes large an unwieldly * complex behaviors that need to be wrapped in callables depend on the underlying views handling callable arguments (which isn't implemented universally) * There's no such thing as a simple extension -- you can't easily say "use that view, but change one argument"; you have to reproduce the full argument list. * .. and much more Moving to a class-based structure means that the complexities of defining and customizing generic views can be handled by subclassing. urls.py returns to being a simple process of pointing at a class. Extension logic can be arbitrarily complex, abstracted behind methods on the generic class. It also means we can provide an entry point for HTTP method-based dispatch -- e.g., a GET() method to handle GET requests, POST() to handle posts, etc. == The problem == However, the devil is in the detail. There are several issues that any class-based view solution needs to address: * '''Deployment''': How do you deploy an instance of the view * '''URLResolver interaction''': Does the approach require any special handling in URLResolver to identify and/or instantiate the view? * '''Subclassing''': How easy is it to subclass and override a specific method on a view class? * '''Thread safety''': Does each request get a clean instance of self to work with? If so, how is this achieved? * '''Ease of testing''': Does testing the view require any special handling (such as a specialized method to instantiate the view) * '''Ease of decoration''': Does the proposed technique pose any impediments to easily decorating views? === View instantiation and thread safety === The [http://groups.google.com/group/django-developers/browse_thread/thread/e23d9a5a58fe5891 most recent django-developers discussion on the topic] discussed many of the available options on instantiating the views and protecting them from threading issues. Here is a summary of the various approaches that have been proposed. === !__call!__() and copy() === [http://github.com/bfirsh/django-class-based-views Implementation] Example usage: {{{ url(r'^detail/author/(?P\d+)/$', views.AuthorDetail(), name="author_detail"), }}} Example class: {{{ class AuthorView(View): def GET(self, request, *args, **kwargs) return self.render_to_response('author_list.html', {'authors': Author.objects.all()}) }}} This approach proposes that an class instance be placed in urls.py; the instance has a !__call!__() method, and when that method is invoked, it takes a shallow copy of the instance defined in urls.py, and returns the result of invoking the request method (e.g., GET()). This achieves thread safety by ensuring that every request is given a clean instance on the view class to work on. No special handling is required in !UrlResolver -- the class instance is a callable, so it appears just like an existing view function. Arguments against: * The "copy on !__call!__()" approach is a little messy. Normal Python idiom wouldn't lead users to expect that !__call!__() would cause a copy to be created. However, this is entirely hidden by the implementation. === !__new!__() === [http://github.com/fitzgen/django-class-based-views Implementation] Example usage: {{{ url(r'^detail/author/(?P\d+)/$', views.AuthorDetail, name="author_detail"), }}} Example class: {{{ class AuthorView(View): def GET(self, request, *args, **kwargs) return self.render_to_response('author_list.html', {'authors': Author.objects.all()}) }}} This approach is much the same as the !__copy!__() on !__call!__() approach, except that !__new!__() is used to create the new instance. Arguments against: * You can't use arguments to __init__() to instantiate a class view * x = !AuthorView() returns x as a HTTPResponse, not an !AuthorView instance. This violates expectations of normal class usage. === HTTPResponse subclassing === [http://github.com/roalddevries/class_based_views/ Implementation] This approach exploits the fact that the aim of a view is to produce a HttpResponse instance; so it shortcuts the process, and makes a 'view' the constructor for the !HttpResponse. Arguments against: * Binds the Response object the concept of a view. A view isn't 'is-a' response, in the OO-sense. * Makes it difficult or impossible to subclass !HttpResponse and use that subclass * Makes it difficult to use !HttpResponse-returning methods; you can't just call out to a subview/utility method that returns a !HttpResponse instance, because the view needs to return 'self'. === !UrlResolver view instantiation === Rather than try to make the view look like a callable, this approach modifies !UrlResolver so that it can identify when a class-based view is in use, and instantiate an instance when one is detected. Arguments against: * Requires special cases in !UrlResolver which almost inevitably involve isinstance(!ViewClass) or some analog. === Recommendation === Based on these discussions, plus in-person discussions at !DjangoCon.eu, !__copy!__() on !__call!__() appears to be a slight front runner, with !__new!__() as a close runner up. == How to help == Class-based views are being developed as a [http://github.com/bfirsh/django-class-based-views separate application on Github]. There are a few things yet to be done: * A simple readme for getting started * Testing it in real applications * More test coverage * Support for ModelForms that mimics the current generic views * Full documentation Fork the Github project if you want to help out.