views:

321

answers:

5

I have this decorator, used to decorate a django view when I do not want the view to be executed if the share argument is True (handled by middleware)

class no_share(object):
    def __init__(self, view):
        self.view = view

    def __call__(self, request, *args, **kwargs):
        """Don't let them in if it's shared"""

        if kwargs.get('shared', True):
            from django.http import Http404
            raise Http404('not availiable for sharing')

        return self.view(request, *args, **kwargs)

It currently works like this:

@no_share
def prefs(request, [...])

But I'm wanting to expand the functionality a little bit, so that it will work like this:

@no_share('prefs')
def prefs(request, [...])

My question is how can I modify this decorator class so that it accepts extra arguments?

+1  A: 
class no_share(object):
    def __init__(self, foo, view):
        self.foo = foo
        self.view = view
Mez
I should have mentioned, I tried that already, but I think django works differently or something because that didn't work. I tried to put `print "blah"` in the init, but all I got was a bunch of 'blah's when I started the dev server. No more 'blah's after that...
nbv4
That's expected, __init__ is only called once, upon the definition of each view function you decorate with this. The __call__ is called every time your server executes the view function to respond to a request.This answer is in the right direction, see my answer for a more complete answer specific for your case.
taleinat
+4  A: 

I hope this article by Bruce Eckel helps.

Upd: According to the article your code will look like this:

class no_share(object):
    def __init__(self, arg1):
        self.arg1 = arg1

    def __call__(self, f):
        """Don't let them in if it's shared"""

        # Do something with the argument passed to the decorator.
        print 'Decorator arguments:', self.arg1

        def wrapped_f(request, *args, **kwargs):
            if kwargs.get('shared', True):
                from django.http import Http404
                raise Http404('not availiable for sharing')
            f(request, *args, **kwargs)            
        return wrapped_f

to be used as desired:

@no_share('prefs')
def prefs(request, [...])
Li0liQ
A: 

Since it seems you're getting this wrong somewhere, here's a more complete example which may help you see what you're doing wrong. Using this as a drop-in should work.

class no_share(object):
    def __init__(self, view, attr_name):
        self.view = view
        self.attr_name = attr_name

    def __call__(self, request, *args, **kwargs):
        """Don't let them in if it's shared"""

        if kwargs.get(self.attr_name, True):
            from django.http import Http404
            raise Http404('not availiable for sharing')

        return self.view(request, *args, **kwargs)
taleinat
+4  A: 

The Bruce Eckel article that Li0liQ mentioned should be helpful in figuring this out. Decorators with and with out arguments behave slightly differently. The big difference is that when you pass arguments the __call__ method gets called once on __init__ and it is supposed to return a function that will be called whenever the decorated function is called. When there are no arguments, the __call__ method gets called every time the decorated function is called.

What does this mean for you? The way that __init__ and __call__ are called for a @no_arg_decorator is different than they are called for a @decorator('with','args').

Here are two decorators that might do the trick for you. You can get away with just the @no_share_on(...) decorator as long as you always use it with the parentheses.

def sharing_check(view, attr_name, request, *args, **kwargs):
    if kwargs.get(attr_name, True):
        from django.http import Http404
        raise Http404('not availiable for sharing')

    return view(request, *args, **kwargs)

class no_share(object):
    """A decorator w/o arguments.  Usage:
    @no_share
    def f(request):
        ...
    """
    def __init__(self, view):
        self.view = view

    def __call__(self, request, *args, **kwargs):
        return sharing_check(self.view, 'sharing', request, *args, **kwargs)

class no_share_on(object):
    """A decorator w/ arguments.  Usage:
    @no_share_on('something')
    def f(request):
        ...
    --OR--
    @no_share_on()
    def g(request):
        ...
    """
    def __init__(self, attr_name='sharing'):
        self.attr_name = attr_name

    def  __call__(self, view):
        def wrapper_f(request, *args, **kwargs):
            return sharing_check(view, self.attr_name, request, *args, **kwargs)
istruble
+1  A: 

I think closure may works here.

def no_share(attr):
    def _no_share(decorated):
        def func(self, request, *args, **kwargs):
            """Don't let them in if it's shared"""

            if kwargs.get(attr, True):
                from django.http import Http404
                raise Http404('not availiable for sharing')

            return decorated(request, *args, **kwargs)
        return func
    return _no_share
Satoru.Logic