Code

Changes between Version 2 and Version 3 of ClassBasedViews


Ignore:
Timestamp:
10/01/10 08:17:35 (4 years ago)
Author:
andrewgodwin
Comment:

Adding a few more "bikeshed-worthy" topics, and a reasonable first recommendation for each.

Legend:

Unmodified
Added
Removed
Modified
  • ClassBasedViews

    v2 v3  
    2828The [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. 
    2929 
    30 === !__call!__() and copy() === 
     30==== !__call!__() and copy() ==== 
    3131 
    3232[http://github.com/bfirsh/django-class-based-views Implementation] 
     
    5151 * 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. 
    5252 
    53 === !__new!__() ===  
     53==== !__new!__() ==== 
    5454 
    5555[http://github.com/fitzgen/django-class-based-views Implementation] 
     
    7373 * x = !AuthorView() returns x as a HTTPResponse, not an !AuthorView instance. This violates expectations of normal class usage. 
    7474 
    75 === HTTPResponse subclassing === 
     75==== HTTPResponse subclassing ==== 
    7676 
    7777[http://github.com/roalddevries/class_based_views/ Implementation] 
     
    8484 * 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'. 
    8585 
    86 === !UrlResolver view instantiation === 
     86==== !UrlResolver view instantiation ==== 
    8787 
    8888Rather 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. 
     
    9191 * Requires special cases in !UrlResolver which almost inevitably involve isinstance(!ViewClass) or some analog. 
    9292 
    93 === Recommendation === 
     93==== Recommendation ==== 
    9494 
    9595Based 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. 
     96 
     97=== Class Hierarchy === 
     98 
     99There are several ways to lay out the tree of classes that will make up both the base views and the new generic views. 
     100 
     101The current recommended approach is to use mixins to achieve certain parts of functionality (e.g. "rendering a form", "accepting form arguments", etc.), then combine these into generic views. While mixins are a relatively unused part of Python, and multiple inheritance can cause some odd bugs, the idea is to have the fact mixins are used as more of an implementation detail rather than part of the public API. 
     102 
     103=== Method-based dispatch === 
     104 
     105This involves having the base view automatically call self.GET for GET requests, self.POST for POST requests, and so forth. This has the advantage of saving boilerplate for several types of view, but the disadvantage of getting in the way if you do want to share a lot of code between GET and POST methods. 
     106 
     107The recommended solution is that the very base view have only a dispatch() method (or a similar name) that gets called per-request, and then a subclass that also ships with Django that does method-based dispatch, which would probably be used by a lot of the class-based generic views. 
     108 
     109=== Storing request, args, kwargs on self === 
     110 
     111One more controversial move is, if we have one instance per request (which is looking more likely), whether the request object and any positional/keyword arguments from the URL should be only passed around in the actual function calls, or stored on self. 
     112 
     113Advantages: 
     114 - Allows much less fragile code (functions are far less subclassable if it's been decided in advance they will never see the request object) 
     115 - Cleaner method signatures 
     116 
     117Disadvantages: 
     118 - Won't work if there's not one instance per request 
     119 
     120The current recommendation is that, if one instance per request is chosen, that these are stored on the instance of the view (using the names `request`, `args` and `kwargs`), and that the `dispatch()` and `GET()`, `POST()`, etc. methods still take these as parameters, but all other methods (like `render()`, or `get_form()`) don't. 
     121 
     122=== Methods for everything === 
     123 
     124Some attempts contain methods for everything - one to get the context instance, one to get the context data, one to get the whole context object, and so on. In real-life usage, this turns out to be both very verbose to override as well as being mostly unused (if I want to override how things are rendered, it's a lot easier to just provide a new `render()` function than to override five different other methods, and the logic flow can be changed as well). 
     125 
     126For this reason, the current recommendation is to break things down into moderately-sized chunks, but not too small - 5 lines or more. Things like template names/patterns should probably be provided as overrideable attributes on the class, however (just not which context instance one should use). 
     127 
     128=== Justification === 
     129 
     130There are quite a few sets of class-based views out there already; they include: 
     131 
     132 - Andrew Godwin's baseviews (https://bitbucket.org/andrewgodwin/baseviews/), which was developed alongside a project being built, and has some information about why certain patterns were chosen in the README. 
     133 - bkondle's django-baseviews (http://github.com/bkonkle/django-baseviews) 
    96134 
    97135== How to help ==