Middleware class vs decorators (proposal?)

1,501 views
Skip to first unread message

Amit Upadhyay

unread,
Apr 13, 2008, 7:05:02 AM4/13/08
to django-d...@googlegroups.com
Hi,

I was wondering about the reason that middleware classes were used instead of decorators to implement middleware functionality. One of the use cases that lead me into thinking about it is that I was looking for a way to have middleware apply only to views of one particular app [facebook app for example]. The MiddlewareNotUsed exception that is used by DebugMiddleware is very limited, vs if it was a decorator, the decorator would have the view passed and it could have taken a decision based on the module path of the view.

The other use case I thought of was: on my site, there are some background processes running intermittently, and the  load my mysql server, and abt 20-30 times a day on my site we get an error abt mysql server not responding. I imagined writing a middleware that will catch this exception, and sleep for a few secs and then call the view again, but I realized the current design of middleware does not allow that, and the only way to do this would be to send a http302 or something, which would be useless for POST requests and would be dangerous if website is loaded due to some reason [it will lead to recursive 302, adding up lot of load on the website]. With middleware I would have passed the actual view, and I would have caught the exception and in excpetion handler trying to call the view one more time before giving up.

This is roughly how a middleware decorator would look like:

def my_middleware(view):
    if not shud_apply_on_this_view(view): return view # this is part of __init__ currently, without access to view.
    def decorated(request, *args, **kw):
         x, y, z = get_xyz_from_request(request) # this is process_request phase, without access to args, kw etc.
         try:
               resp = view(*args, **kw)
         except TheExceptionIAmInterestedIn:
               do_something_with_it()
               # this is process_exception, but much more natural python, than to do if isinstance(ex,  MyExcp)
               # plus i can call view again if i feel like.
         do_something_with_xyz(x, y, z) # this is process_response, having access to x, y, z is more natural than to attach them to self before and after the call of view
         return resp
     return decorated


--
Amit Upadhyay
Vakow! www.vakow.com
+91-9820-295-512

Alex Koshelev

unread,
Apr 13, 2008, 8:54:26 AM4/13/08
to Django developers

Amit Upadhyay

unread,
Apr 14, 2008, 4:02:05 AM4/14/08
to django-d...@googlegroups.com
On Sun, Apr 13, 2008 at 6:24 PM, Alex Koshelev <daev...@gmail.com> wrote:

This wont really help much as I will still have to apply the decorators manually to all views instead of enabling them programmatically, and I can still not call the view more than once etc.

I understand that it is probably too late for such a change, with a one book published and two in the pipeline, but was wondering if there was some advantage of the current design over this that I am missing.

PS: This is another advantage of having middleware as decorators, we won't need that helper :-)

James Bennett

unread,
Apr 14, 2008, 4:30:48 AM4/14/08
to django-d...@googlegroups.com
On Sun, Apr 13, 2008 at 6:05 AM, Amit Upadhyay <upad...@gmail.com> wrote:
> I was wondering about the reason that middleware classes were used instead
> of decorators to implement middleware functionality.

There are two cases, each with drawbacks:

1. Whatever system is available to provide additional processing is by
default applied only to a single specific view (e.g., a decorator).
The drawback is that you will inevitably want an easy way to say
"apply this everywhere" instead of manually applying the decorator to
every view.

2. Whatever system is available to provide additional processing is by
default applied to every view (e.g., a middleware class). The drawback
is that you will inevitably want an easy way to say "apply this only
to this specific view" instead of writing and maintaining a separate
copy of the code.

Both of these solutions' drawbacks can be solved by helper code: a
utility which transforms a decorator into a middleware class could be
provided, or a utility which transforms a middleware class into a
decorator could be provided.

Django has chosen option 2, and provided the helper method to go from
middleware -> decorator as needed.

This does have some advantages:

1. It's easier to lay out a readable API that determines when a
particular piece of code gets executed; figuring out whether a
decorator turns into a 'process_request' or a 'process_response', for
example, would be tricky, but plucking out a middleware class' methods
and applying them at the correct points in a decorator is easy.

2. Providing a dedicated global middleware API opens up some hooks
that wouldn't otherwise be available; for example, when going from a
middleware to a decorator you can still have a 'process_request'
phase, but it doesn't occur at the same time and you can't do some of
the useful tricks that a real middleware can do -- for example, you
can't tweak request.urlconf to change URL resolution, because by the
time a decorated view comes into play the URL resolution phase is
over.


--
"Bureaucrat Conrad, you are technically correct -- the best kind of correct."

James Bennett

unread,
Apr 14, 2008, 4:32:04 AM4/14/08
to django-d...@googlegroups.com
On Mon, Apr 14, 2008 at 3:30 AM, James Bennett <ubern...@gmail.com> wrote:
> the useful tricks that a real middleware can do -- for example, you
> can't tweak request.urlconf to change URL resolution, because by the
> time a decorated view comes into play the URL resolution phase is
> over.

Well. I should say you can't do them as easily. You could, I suppose,
write a decorator which imports the URL resolution machinery and does
a fresh match based on new URL patterns, then calls the view that
comes out of that, but it'd be awfully tedious compared to the way it
works in middleware.

Amit Upadhyay

unread,
Apr 14, 2008, 4:41:58 AM4/14/08
to django-d...@googlegroups.com
On Mon, Apr 14, 2008 at 2:00 PM, James Bennett <ubern...@gmail.com> wrote:

On Sun, Apr 13, 2008 at 6:05 AM, Amit Upadhyay <upad...@gmail.com> wrote:
> I was wondering about the reason that middleware classes were used instead
> of decorators to implement middleware functionality.

There are two cases, each with drawbacks:

1. Whatever system is available to provide additional processing is by
default applied only to a single specific view (e.g., a decorator).
The drawback is that you will inevitably want an easy way to say
"apply this everywhere" instead of manually applying the decorator to
every view.

... SNIP ...

You got me wrong. I am talking about pre-applying the "middleware-decorators" to all views found thru urls.py processing.

Marty Alchin

unread,
Apr 14, 2008, 8:51:44 AM4/14/08
to django-d...@googlegroups.com
On Sun, Apr 13, 2008 at 7:05 AM, Amit Upadhyay <upad...@gmail.com> wrote:
> I was wondering about the reason that middleware classes were used instead
> of decorators to implement middleware functionality. One of the use cases
> that lead me into thinking about it is that I was looking for a way to have
> middleware apply only to views of one particular app [facebook app for
> example]. The MiddlewareNotUsed exception that is used by DebugMiddleware is
> very limited, vs if it was a decorator, the decorator would have the view
> passed and it could have taken a decision based on the module path of the
> view.
>
> The other use case I thought of was: on my site, there are some background
> processes running intermittently, and the load my mysql server, and abt
> 20-30 times a day on my site we get an error abt mysql server not
> responding. I imagined writing a middleware that will catch this exception,
> and sleep for a few secs and then call the view again, but I realized the
> current design of middleware does not allow that, and the only way to do
> this would be to send a http302 or something, which would be useless for
> POST requests and would be dangerous if website is loaded due to some reason
> [it will lead to recursive 302, adding up lot of load on the website]. With
> middleware I would have passed the actual view, and I would have caught the
> exception and in excpetion handler trying to call the view one more time
> before giving up.

Call me crazy, but I'm not seeing anything here that can't be done in
the process_view() stage of middleware. It receives the incoming
request, the view that's about to be executed (so you can get its
module path), as well as the arguments that will be applied to it.

The code you specified could actually be run inside there, executing
the view directly if you like, then returning the response directly
from process_view(), bypassing the rest of the middleware phase. It
doesn't sound like a pleasant situation to me, but neither does what
you're trying to accomplish.

I really think you're looking for a middleware that implements
process_view(). Look into it,[1] if you haven't already.

-Gul

[1] http://www.djangoproject.com/documentation/middleware/#process-view

Michael Elsdörfer

unread,
Apr 17, 2008, 11:45:36 PM4/17/08
to Django developers
On Apr 14, 10:02 am, "Amit Upadhyay" <upadh...@gmail.com> wrote:
> as I will still have to apply the decorators
> manually to all views instead of enabling them programmatically, and I can
> still not call the view more than once etc.

I needed something similar a while ago and came up with this:

http://www.djangosnippets.org/snippets/532/

Michael
Reply all
Reply to author
Forward
0 new messages