On Fri, 2010-10-01 at 12:16 +0200, Johannes Dollinger wrote:
> Am 01.10.2010 um 07:26 schrieb Russell Keith-Magee:
> > I've just added a summary of the last thread on class-based views
> [1].
> > This summary isn't 100% complete -- any contributions from
> interested
> > parties are welcome. Try to keep opinions to a minimum; this page is
> > about documenting the strengths and weaknesses of various
> approaches,
> > not about expressing opinions. In the same way that CSRF [2] and
> > session-messages [3] have good wiki pages describing the design
> > considerations, we need to be able to explain to future generations
> > why class-based views are the way they are.
> 
> Could you (or anyone knowledgable) add a section, that explains why
> each request should have its own view instance?
> The thread-safety argument alone does not suffice: if all _request_
> state would be stored on request instead of the view, you wouldn't
> need new instances per request. You could also pass around state
> explicitly - which admittedly gets messy quickly.
> So is this purely about preventing users from shooting themselves in
> the foot? (hyperbole: Will there be protection from globals in django
> 1.4?)

It's not just about stopping users from shooting themselves in the foot,
it's about helping them to do the right thing easily.  Without this kind
of protection, it will be harder for *anyone*, including experienced
developers who are aware of the problem, to do things correctly. It's
like the autoescaping in templates - I *know* that I should use the
'escape' filter, but without autoescaping it is hard for anyone to do it
right all the time.

One alternative that has been suggested is to pass state around
explicitly, which is definitely best practice, but in many cases might
not be possible (e.g. if you are working with someone else's base class,
and in one overridden method you want to set up some objects which ought
to be available to other methods). You could also attach stateful data
to the request object, but attaching random bits of data to the request
is surely just as ugly a solution as the self.copy() call, especially if
you need to avoid names clashes with all the other attributes attached
to request.

With regards to doing a shallow copy, it should be noted that at the
point the copy is made, the only data attached to the object is data
that has been attached in the constructor. It is of course possible that
methods within the view might mutate such data, but it seems more likely
that it will be used as immutable configuration data. 

However, I have already seen some example code that might fall foul of
this problem - someone gave the example of storing a queryset on the
object in the __init__. This will get implicitly mutated (the cache is
filled) when it is used the first time. This could lead to frustrating
problems that wouldn't be found in testing, and doing copy() on the
instance won't help.

So, in light of the fact that developers *will* need to be aware of this
issue, I'd like to tentatively suggest an explicit solution which is
nonetheless easy to use and get right. We could have an explicit 'state'
object that is thrown away for every new request, something like this:

class State(object):
    pass

class View(object):
    def __call__(self, request, *args, **kwargs):
        """
        Main entry point for a request-response process.
        """
        self.state = State()
        self.request = request
        self.args = args
        self.kwargs = kwargs
        resp = self.dispatch(request, *args, **kwargs)
        # let any state get GC'd immediately:
        del self.state 
        del self.request
        del self.args
        del self.kwargs
        return resp

We document the issue, warn people not to store state on the instance
itself, but tell them that if they must have stateful data, it should be
attached to self.state instead of self, and they will be OK.  They might
still be bitten if they put mutable configuration data into the __init__
and it gets mutated, but we have made it easy to do the right thing -
doing 'self.state.x' is only slightly harder than 'self.x'

Thoughts?

Luke

-- 
"Despair: It's always darkest just before it goes pitch black." 
(despair.com)

Luke Plant || http://lukeplant.me.uk/

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-develop...@googlegroups.com.
To unsubscribe from this group, send email to 
django-developers+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en.

Reply via email to