3

I'm trying to convert some of my django views over from function based views to class based views and I've run into a small problem.

My OO is kind of weak and I think the problem is that I've lost track of where things are going.

I have a custom login decorator that I need on the views so I have...

First I have the View class from this example http://www.djangosnippets.org/snippets/760/

Then my view class looks like this...

class TopSecretPage(View):
    @custom_login
    def __call__(self, request, **kwargs):
        #bla bla view stuff...
        pass

The problem is that my decorator can't access request.session for some reason...

My decorator looks like this...

def myuser_login_required(f):
    def wrap(request, *args, **kwargs):

        # this check the session if userid key exist,
        # if not it will redirect to login page

        if 'field' not in request.session.keys():
        return wrap

I think it's something simple that I'm missing so thanks for your patience everyone!

UPDATE: Ok so here's the error that I get...

"ViewDoesNotExist: Tried TopSecretPage in module projectname.application.views. Error was: type object 'TopSecretPage' has no attribute 'session'"

I simplified the decorator as well to look like this....

def myuser_login_required(request, *args, **kwargs):


    # this check the session if userid key exist,
    # if not it will redirect to login page

    if 'username' not in request.session.keys():
        return  HttpResponseRedirect(reverse("login-page"))

    return True
Thomas Schultz
  • 2,446
  • 3
  • 25
  • 36

5 Answers5

6

The correct way to do this for any decorator applied to any class-based view method is to use django.utils.decorators.method_decorator(). I'm not sure when method_decorator() was introduced but here is an example/update in the Django 1.2 release notes. Use it like this:

from django.utils.decorators import method_decorator

class TopSecretPage(View):
    @method_decorator(custom_login)
    def __call__(self, request, **kwargs):
        #bla bla view stuff...
        pass
JCotton
  • 11,650
  • 5
  • 53
  • 59
Gert Steyn
  • 2,194
  • 1
  • 15
  • 8
  • Can you cite that? Are you saying that class based views are the way django 1.2 says to do views? I've since moved away from class based views for maintenance reasons. – Thomas Schultz Jul 05 '11 at 18:55
  • Have a look at this section of the 1.2 release notes: – Gert Steyn Jul 15 '11 at 09:24
  • https://docs.djangoproject.com/en/1.3/releases/1.2/#user-passes-test-login-required-and-permission-required – Gert Steyn Jul 15 '11 at 09:25
  • @ThomasSchultz what maintenance reasons? Are you reccomending people avoid class based views, I've had no issues with them so far. – Stephen Paulger Mar 15 '12 at 12:01
  • 1
    I suspect it's more of a discipline thing. We've found that class based views tend to grow quickly and add a lot of complexity. Also, there's a lot of temptation to start duplicating code more. Then you start having things like "base" view classes and things. We've found that we can use function views that are easier to maintain and are far less complex and easier to debug. Just our experience though. Class views do work, we used them for almost 2 years. – Thomas Schultz Mar 21 '12 at 14:24
3

The problem is that your wrapper expects "request" as the first argument, but a method on a class always takes "self" as the first argument. So in your decorator, what it thinks is the request object is actually TopSecretPage itself.

Either Vinay or artran's solutions should work, so I won't repeat them. Just thought a clearer description of the problem might be helpful.

Carl Meyer
  • 122,012
  • 20
  • 106
  • 116
2

This problem has come up before. A solution is included which might work for you.

Update: Example method with decorator:

class ContentView(View):

    # the thing in on_method() is the actual Django decorator
    #here are two examples
    @on_method(cache_page(60*5))
    @on_method(cache_control(max_age=60*5))
    def get(self, request, slug): # this is the decorated method
        pass #in here, you access request normally
Vinay Sajip
  • 95,872
  • 14
  • 179
  • 191
  • Hmm, well like I said my oop skills are weak and I actually read that before posting this question. I don't see how to access the request object in the decorator function from that example. – Thomas Schultz Sep 15 '09 at 20:26
  • Thanks for updating! but I still have the problem haha. I'll update my question to try and show it... – Thomas Schultz Sep 16 '09 at 12:56
  • For Django 1.5 it's `@method_decorator(cache_control(max_age=86400)) def get(self, request, *args, **kwargs):` – chrishiestand Dec 05 '13 at 04:06
1

Instead of using the decorator on the view you could decorate the url.

For example in urls.py:

from my_decorators import myuser_login_required
from my_views import TopSecretPage

urlpatterns = patterns('', 
    (r'^whatever-the-url-is/$', myuser_login_required(TopSecretPage), {}),
)

You may need to play with that a little but it's about right.

artran
  • 310
  • 2
  • 10
  • I think this solution should work, but you need to instantiate TopSecretPage: myuser_login_required(TopSecretPage()) – Carl Meyer Sep 16 '09 at 13:17
1

This is effectively a duplicate of Django - Correct way to pass arguments to CBV decorators? which describes the correct way of addressing this. The correct way of doing this for django 1.9 is as follows:

@method_decorator(myuser_login_required(), name='dispatch')
class TopSecretPage(View):
    ..
Community
  • 1
  • 1
Ric W
  • 2,232
  • 2
  • 18
  • 12