Changes between Initial Version and Version 1 of ClassBasedViews


Ignore:
Timestamp:
Sep 30, 2010, 11:58:36 PM (14 years ago)
Author:
Russell Keith-Magee
Comment:

Summary of work on Class-based generic views

Legend:

Unmodified
Added
Removed
Modified
  • ClassBasedViews

    v1 v1  
     1= Class-Based Views =
     2
     3The 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.
     4
     5== The brief ==
     6
     7At 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:
     8
     9 * urls.py rapidly becomes large an unwieldly
     10 * complex behaviors that need to be wrapped in callables depend on the underlying views handling callable arguments (which isn't implemented universally)
     11 * 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.
     12 * .. and much more
     13
     14Moving 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.
     15
     16== The problem ==
     17
     18However, the devil is in the detail. There are several issues that any class-based view solution needs to address:
     19
     20 * '''Deployment''': How do you deploy an instance of the view
     21 * '''URLResolver interaction''': Does the approach require any special handling in URLResolver to identify and/or instantiate the view?
     22 * '''Subclassing''': How easy is it to subclass and override a specific method on a view class?
     23 * '''Thread safety''': Does each request get a clean instance of self to work with? If so, how is this achieved?
     24 * '''Ease of testing''': Does testing the view require any special handling (such as a specialized method to instantiate the view)
     25 * '''Ease of decoration''': Does the proposed technique pose any impediments to easily decorating views?
     26
     27The [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. Here is a summary of the various approaches that have been proposed.
     28
     29=== !__call!__() and copy() ===
     30
     31[http://github.com/bfirsh/django-class-based-views Implementation]
     32
     33Example usage:
     34{{{
     35    url(r'^detail/author/(?P<pk>\d+)/$', views.AuthorDetail(), name="author_detail"),
     36}}}
     37
     38Example class:
     39{{{
     40class AuthorView(View):
     41    def GET(self, request, *args, **kwargs)
     42        return self.render_to_response('author_list.html', {'authors': Author.objects.all()})
     43}}}
     44
     45This 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.
     46
     47No special handling is required in !UrlResolver -- the class instance is a callable, so it appears just like an existing view function.
     48
     49Arguments against:
     50 * 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.
     51
     52=== !__new!__() ===
     53
     54[http://github.com/fitzgen/django-class-based-views Implementation]
     55
     56Example usage:
     57{{{
     58    url(r'^detail/author/(?P<pk>\d+)/$', views.AuthorDetail, name="author_detail"),
     59}}}
     60
     61Example class:
     62{{{
     63class AuthorView(View):
     64    def GET(self, request, *args, **kwargs)
     65        return self.render_to_response('author_list.html', {'authors': Author.objects.all()})
     66}}}
     67
     68This approach is much the same as the !__copy!__() on !__call!__() approach, except that !__new!__() is used to create the new instance.
     69
     70Arguments against:
     71 * You can't use arguments to __init__() to instantiate a class view
     72 * x = !AuthorView() returns x as a HTTPResponse, not an !AuthorView instance. This violates expectations of normal class usage.
     73
     74=== HTTPResponse subclassing ===
     75
     76[http://github.com/roalddevries/class_based_views/ Implementation]
     77
     78This 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.
     79
     80Arguments against:
     81 * Binds the Response object the concept of a view. A view isn't 'is-a' response, in the OO-sense.
     82 * Makes it difficult or impossible to subclass !HttpResponse and use that subclass
     83 * 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'.
     84
     85=== !UrlResolver view instantiation ===
     86
     87Rather 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.
     88
     89Arguments against:
     90 * Requires special cases in !UrlResolver which almost inevitably involve isinstance(!ViewClass) or some analog.
     91
     92= Recommendation =
     93
     94Based 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.
Back to Top