For many - but not all - of my views I have to do some validation to make sure the user that is logged in has access to the object they are trying to access. For 30+ views I have this code:
def whatever_view_name(request, id, access_id):
check = Access.objects.filter(user=request.user, id=access_id)
if check:
access_object = check[0]
else:
return redirect(reverse("create_new_access_object"))
.... and now my view-specific code will follow ...
So I need to check if a particular database record (Access) exists for this particular user. This code is repeated a lot, which does not seem to be right. I've been thinking about using middleware, but there are two problems: a) I need to use this object in the view (see variable access_object so I fear I'd have to query it twice if I put it in the middleware), and b) I don't need to do this ALWAYS so I wonder how to only run it for some views and not all if this is middleware.
Any thoughts?
You can write a decorator for this:
from functools import wraps
def check_access(function):
#wraps(function)
def wrap(request, id, access_id, *args, **kwargs):
check = Access.objects.filter(user=request.user, id=access_id)
if check.exists():
return function(request, id, access_id, *args, **kwargs)
else:
return redirect(reverse("create_new_access_object"))
return wrap
# usage
#check_access
def whatever_view_name(request, id, access_id):
return ...
One way that I can think of is using inheritance. We can refactor out the common stuff into a super view class and then extend the same in child view classes.
Something like this :
We can have a super class like this
class AccessVerifiedView(View):
def get(self, request, *args, **kwargs):
check = Access.objects.filter(user=request.user, id=kwargs["access_id"])
if check:
access_object = check[0]
self.verified_get(access_object)
else:
return redirect(reverse("create_new_access_object"))
def verified_get(self, access_object):
raise NotImplementedError
Then we can extend that class and the use in our views.
class MyView(AccessVerifiedView):
def verified_get(self, access_object):
return access_object
This approach see bit more readable. Anyone seeing the code can see the super class and understand the code flow.
Other few ways to do it is
Decorator : We can have a decorator which will do the same thing. And then we can decorate the view which we want to verify.
Related
Say I have a route like this, that renders lol.html with b set to None:
#app.route('/foo/')
def foo():
return render_template('lol.html', a='a', b=None)
I want to make another route, that can intercept one route and add some variables to it before returning. Something like this:
#app.route('/bar/')
def bar():
intercepted = make_response(foo())
# do something signficant to extend the logic of foo
flask.add_var_to_resp(intercepted, b='b')
How should this be accomplished in flask?
#jsbueno, I'm adding this to clarify your answer, which I cannot do in a comment:
#app.route('/foo/')
def foo(renderer=render_template):
return renderer('foo.html', a='a')
#app.route('/bar/')
def bar():
def renderer(template, *args, **kwargs):
return render_template(template, *args, b='b', **kwargs)
return foo(renderer)
Flask is mostly pure Python, with very little magic - so there are a series of strategies you can use.
Maybe the simplest is to have the primary view to accept optional
function arguments, and call that one from your extended views:
#app.route('/foo/')
def foo(b=None):
return render_template('lol.html', a='a', b=b)
#app.route('/bar/')
def bar():
return foo(b="b")
update:
If you don't want the original view to incorporate the extensions logic, you have to keep in mind that views returns a rendered template, that is not extensible - you can't simply add more lines inside the foo view.
One alternative is to turn the renderer itself into a lazy object - that delays the call to flask actual render_template so that it is modifiable. For a simple view to keep working withoug bein aware of this modified render_template, it could opionally receive the renderer as a parameter.
So,
#app.route('/foo/')
def foo(renderer=render_template):
return renderer('lol.html', a='a')
#app.route('/bar/')
def bar():
def renderer(template, *args, **kw):
return render_template(template, *args, b="b", **kw)
return foo(renderer)
Should work for you - you can make the "renderer" be a smarter object where you can plug more things as needed.
I'm trying to do cache_page with class based views (TemplateView) and i'm not able to. I followed instructions here:
Django--URL Caching Failing for Class Based Views
as well as here:
https://github.com/msgre/hazard/blob/master/hazard/urls.py
But I get this error:
cache_page has a single mandatory positional argument: timeout
I read the code for cache_page and it has the following:
if len(args) != 1 or callable(args[0]):
raise TypeError("cache_page has a single mandatory positional argument: timeout")
cache_timeout = args[0]
which means it wont allow more than 1 argument. Is there any other way to get cache_page to work?? I have been digging into this for sometime...
It seems like the previous solutions wont work any longer
According to the caching docs, the correct way to cache a CBV in the URLs is:
from django.views.decorators.cache import cache_page
url(r'^my_url/?$', cache_page(60*60)(MyView.as_view())),
Note that the answer you linked to is out of date. The old way of using the decorator has been removed (changeset).
You can simply decorate the class itself instead of overriding the dispatch method or using a mixin.
For example
from django.views.decorators.cache import cache_page
from django.utils.decorators import method_decorator
#method_decorator(cache_page(60 * 5), name='dispatch')
class ListView(ListView):
...
Django docs on decorating a method within a class based view.
yet another good example CacheMixin
from cyberdelia github
class CacheMixin(object):
cache_timeout = 60
def get_cache_timeout(self):
return self.cache_timeout
def dispatch(self, *args, **kwargs):
return cache_page(self.get_cache_timeout())(super(CacheMixin, self).dispatch)(*args, **kwargs)
usecase:
from django.views.generic.detail import DetailView
class ArticleView(CacheMixin, DetailView):
cache_timeout = 90
template_name = "article_detail.html"
queryset = Article.objects.articles()
context_object_name = "article"
You can add it as a class decorator and even add multiple using a list:
#method_decorator([vary_on_cookie, cache_page(900)], name='dispatch')
class SomeClass(View):
...
I created this little mixin generator to do the caching in the views file, instead of in the URL conf:
def CachedView(cache_time=60 * 60):
"""
Mixing generator for caching class-based views.
Example usage:
class MyView(CachedView(60), TemplateView):
....
:param cache_time: time to cache the page, in seconds
:return: a mixin for caching a view for a particular number of seconds
"""
class CacheMixin(object):
#classmethod
def as_view(cls, **initkwargs):
return cache_page(cache_time)(
super(CacheMixin, cls).as_view(**initkwargs)
)
return CacheMixin
Yet another answer, we found this to be simplest and is specific to template views.
class CachedTemplateView(TemplateView):
#classonlymethod
def as_view(cls, **initkwargs): ##NoSelf
return cache_page(15 * 60)(super(CachedTemplateView, cls).as_view(**initkwargs))
Would like to add this:
If you need to use multiple decorators for cache like vary_on_headers and cache_page together, here is one way I did:
class CacheHeaderMixin(object):
cache_timeout = int(config('CACHE_TIMEOUT', default=300))
# cache_timeout = 60 * 5
def get_cache_timeout(self):
return self.cache_timeout
def dispatch(self, *args, **kwargs):
return vary_on_headers('Authorization')(cache_page(self.get_cache_timeout())(super(CacheHeaderMixin, self).dispatch))(*args, **kwargs)
This way cache is stored and it varies for different Authorization header (JWT). You may use like this for a class based view.
class UserListAPIView(CacheHeaderMixin, ListAPIView):
serializer_class = UserSerializer
def get_queryset(self):
return CustomUser.objects.all()
I didn't found a good cache solution for class based views and created my own: https://gist.github.com/svetlyak40wt/11126018
It is a mixin for a class. Add it before the main base class and implement method get_cache_params like that:
def get_cache_params(self, *args, **kwargs):
return ('some-prefix-{username}'.format(
username=self.request.user.username),
3600)
Here's my variation of the CachedView() mixin - I don't want to cache the view if the user is authenticated, because their view of pages will be unique to them (e.g. include their username, log-out link, etc).
class CacheMixin(object):
"""
Add this mixin to a view to cache it.
Disables caching for logged-in users.
"""
cache_timeout = 60 * 5 # seconds
def get_cache_timeout(self):
return self.cache_timeout
def dispatch(self, *args, **kwargs):
if hasattr(self.request, 'user') and self.request.user.is_authenticated:
# Logged-in, return the page without caching.
return super().dispatch(*args, **kwargs)
else:
# Unauthenticated user; use caching.
return cache_page(self.get_cache_timeout())(super().dispatch)(*args, **kwargs)
I have some code that's allows you to define what happens on christmas without knowing anything about the underlying implementation or how the method is called e.g.,
# main.py
import lib.person
person = lib.person.Person()
#person.onchristmas()
def christmas():
print "It's Christmas"
The implementation of the class is something like this:
# lib.person.py
class Person():
def onchristmas(self):
def decorator(f):
self.christmas_handler = f
return f
return decorator
def is_christmas(self):
# called from somewhere else:
self.christmas_handler()
The problem is that I can't import main.py without constructing a person. Similarly I can't move the constructor to be:
person = None
def init():
person = lib.person.Person()
return person
because then person would be NoneType and the decorators won't work. What the correct way to factor this code so that:
I can still use the decorator to let people implement their own christmas action without editing lib.person.py
I can construct person explicitly with init() instead of it happening on import.
EDIT FURTHER DETAIL FROM COMMENTS:
In actual fact there are many different things that can happen not just christmas, and there isn't just one handler per action there might be a number and all must execute:
So:
def onchristmas(self):
def decorator(f):
self.christmas_handler.append(f)
return f
return decorator
def is_christmas(self):
# called from somewhere else:
for h in self.christmas_handler:
h()
Usage: I would like others to be able to specify the behavior of one or more actions without having to know how/when they will be called and ideally further down the line to be able to de-register handlers.
Also I should mention that there will only ever be on instance of Person, I'm not too familiar with static methods and singletons in Python though. Thanks for the help!
The issue is the mixing of your decorator and your state. Why not break your decorator out of the class and allow your users to supply their own function. Such that the only state the decorator relies on is that which is supplied to it. Something similiar to this:
def christmas_config(user_defined_func):
def inner_config(func):
def f(*args, **kwargs):
print 'Hey Yo'
return user_defined_func(func, *args, **kwargs)
return f
return inner_config
def test(func, *args, **kwargs):
print 'This is hairy'
return func(*args, **kwargs)
#christmas_config(test)
def my_func():
print 'test'
my_func()
I'm trying to adopt the Django documentation example on using class based views with mixins in order to be able to make a simple way of downloading the objects in a list view in CSV format, but I am failing miserably and do not really see what I am doing wrong.
I have a view defined as:
class MyObjectList(CSVResponseMixin,
MultipleObjectTemplateResponseMixin,
BaseListView
):
object_type = None
def get_context_data(self, **kwargs):
object_type = self.object_type
...some code...
return context
def render_to_response(self, context, **kwargs):
if self.request.GET.get('format', '') == 'csv':
return CSVReponseMixin.render_to_response(self, context, **kwargs)
else:
return MultipleObjectTemplateResponseMixin.render_to_response(self, context, **kwargs)
the mixin is:
class CSVResponseMixin(object):
def render_to_response(self, ctx, **kwargs):
return self.HttpResponse.render_to_response(self.convert_to_csv(ctx), **kwargs)
def conver_to_csv(ctx):
return do_csv_magic(ctx)
and in urls.py the view is called as:
url(r'^list/foos/$',
MyObjectList.as_view(object_type="someobject", model=models.MyModel),
name="myobjects",
)
However when I try to access the view without the ?format=csv query, I get a TypeError
Exception Value: __init__() got an unexpected keyword argument 'request'
Exception Location: /usr/lib/python2.6/site-packages/django/views/generic/base.py in render_to_response, line 97
EDIT: I added some details to the question and ended up implementing this with a different approach, but I still want to know what I was doing wrong.
In short, you're overdoing it. I'm not sure what is your intention here, but I've learned that the best approach is to find the closest generic view to what you're trying to do and simply extend it in views.py. Examples are many, but I invite you to check my code at https://bitbucket.org/BerislavLopac/resume/src/d7cfcf9c370b/resume_app/myproject/web/views.py.
According to the docs, render_to_response only takes the following arguments: template_name, dictionary, context_instance, mimetype
Therefore within FooResponseMixin when you're calling:
self.HttpResponse.render_to_response(self.mutilate_context(ctx), **kwargs)
You're passing in extra arguments within kwargs that render_to_response doesn't accept. Either remove the **kwargs or assign only what you need from it to variables to pass in to the accepted arguments.
I'm trying to use decorators in order to manage the way users may or may not access resources within a web application (running on Google App Engine). Please note that I'm not allowing users to log in with their Google accounts, so setting specific access rights to specific routes within app.yaml is not an option.
I used the following resources :
- Bruce Eckel's guide to decorators
- SO : get-class-in-python-decorator2
- SO : python-decorators-and-inheritance
- SO : get-class-in-python-decorator
However I'm still a bit confused...
Here's my code ! In the following example, current_user is a #property method which belong to the RequestHandler class. It returns a User(db.model) object stored in the datastore, with a level IntProperty().
class FoobarController(RequestHandler):
# Access decorator
def requiredLevel(required_level):
def wrap(func):
def f(self, *args):
if self.current_user.level >= required_level:
func(self, *args)
else:
raise Exception('Insufficient level to access this resource')
return f
return wrap
#requiredLevel(100)
def get(self, someparameters):
#do stuff here...
#requiredLevel(200)
def post(self):
#do something else here...
However, my application uses different controllers for different kind of resources. In order to use the #requiredLevel decorator within all subclasses, I need to move it to the parent class (RequestHandler) :
class RequestHandler(webapp.RequestHandler):
#Access decorator
def requiredLevel(required_level):
#See code above
My idea is to access the decorator in all controller subclasses using the following code :
class FoobarController(RequestHandler):
#RequestHandler.requiredLevel(100)
def get(self):
#do stuff here...
I think I just reached the limit of my knowledge about decorators and class inheritance :). Any thoughts ?
Your original code, with two small tweaks, should also work. A class-based approach seems rather heavy-weight for such a simple decorator:
class RequestHandler(webapp.RequestHandler):
# The decorator is now a class method.
#classmethod # Note the 'klass' argument, similar to 'self' on an instance method
def requiredLevel(klass, required_level):
def wrap(func):
def f(self, *args):
if self.current_user.level >= required_level:
func(self, *args)
else:
raise Exception('Insufficient level to access this resource')
return f
return wrap
class FoobarController(RequestHandler):
#RequestHandler.requiredLevel(100)
def get(self, someparameters):
#do stuff here...
#RequestHandler.requiredLevel(200)
def post(self):
#do something else here...
Alternately, you could use a #staticmethod instead:
class RequestHandler(webapp.RequestHandler):
# The decorator is now a static method.
#staticmethod # No default argument required...
def requiredLevel(required_level):
The reason the original code didn't work is that requiredLevel was assumed to be an instance method, which isn't going to be available at class-declaration time (when you were decorating the other methods), nor will it be available from the class object (putting the decorator on your RequestHandler base class is an excellent idea, and the resulting decorator call is nicely self-documenting).
You might be interested to read the documentation about #classmethod and #staticmethod.
Also, a little bit of boilerplate I like to put in my decorators:
#staticmethod
def requiredLevel(required_level):
def wrap(func):
def f(self, *args):
if self.current_user.level >= required_level:
func(self, *args)
else:
raise Exception('Insufficient level to access this resource')
# This will maintain the function name and documentation of the wrapped function.
# Very helpful when debugging or checking the docs from the python shell:
wrap.__doc__ = f.__doc__
wrap.__name__ = f.__name__
return f
return wrap
After digging through StackOverflow, and carefully reading Bruce Eckel's guide to decorators, I think I found a possible solution.
It involves implementing the decorator as a class in the Parent class :
class RequestHandler(webapp.RequestHandler):
# Decorator class :
class requiredLevel(object):
def __init__(self, required_level):
self.required_level = required_level
def __call__(self, f):
def wrapped_f(*f_args):
if f_args[0].current_user.level >= self.required_level:
return f(*f_args)
else:
raise Exception('User has insufficient level to access this resource')
return wrapped_f
This does the work ! Using f_args[0] seems a bit dirty to me, I'll edit this answer if I find something prettier.
Then you can decorate methods in subclasses the following way :
FooController(RequestHandler):
#RequestHandler.requiredLevel(100)
def get(self, id):
# Do something here
#RequestHandler.requiredLevel(250)
def post(self)
# Do some stuff here
BarController(RequestHandler):
#RequestHandler.requiredLevel(500)
def get(self, id):
# Do something here
Feel free to comment or propose an enhancement.