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.
Related
I know the suggested way to do this is middleware, but I'd like to implement the following to add in an api_env variable, accessed at run-time, so that my view can access it:
#api_wrapper
def my_view(request):
print api_env # this is the variable I want, such as request.epi_env
And the decorator:
def api_wrapper(func):
def api_inner(request):
request.api_env = 'something'
return func(request)
return api_inner
What's the best way to do this? I have about 100 functions to wrap so I don't want to add in a new parameter for every function but would just like to use the simplest approach to pass that 'env' variable. How should I do it.
You can generalize this to work with an arbitrary number of positional and named parameters. Furthermore you might want to use update_wrapper [Python-doc], to add attributes like csrf_exempt to the "output" view, otherwise the #csrf_except will not be available in the outer function:
from functools import update_wrapper
def api_wrapper(func):
def api_inner(request, *args, **kwargs):
request.api_env = 'something'
return func(request, *args, **kwargs)
update_wrapper(api_inner, func, assigned=())
return api_inner
That being said, this to some extent shows that using class-based views might be better in this case, since then one can define a mixin, and just mix it in the method resolution order (MRO) as a reusable component. In that case, one often do not have to take into account more complicated logic like the parameters, or the attributes that are added to the function, since a class-based view takes care of this itself.
Try this:
def api_wrapper():
def decorator(view_func):
#wraps(view_func)
def _wrapped_view(req, *args, **kwargs):
req.env = 'something'
return view_func(req, *args, **kwargs)
return _wrapped_view
return decorator
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.
I have a FormView for displaying form, and code goes on like this:
class AddProject(FormView):
template_name = "project/add_project.html"
#method_decorator(check_user_type)
def dispatch(self,request, *args, **kwargs):
return super(AddProject,self).dispatch(request, *args, **kwargs)
def get_form_class(self):
return AddProjectForm
def form_valid(self,form):
#do validation, return response
the decorator check_user_type is like this:
def check_user_type(func):
def wrapped_func(request, *args, **kwargs):
kwargs['invalid_user'] = True
return func(request,*args, **kwargs)
return wrapped_func
In my decorator I want To make sure that only certain type of user get to see the form, i.e if request.user.Iam == 'Architect' or request.user.Iam == 'Interior Designer' only see the form and others see a message "Only architects/Interior Designer get to upload photos".For this i want to insert a variable 'invalid_user' to be passed along, depending on which i display the form or the message.
Problem is I am unable to pass the variable :( alongwith it .. and a doubt.. if i have correctly devised the idea to do so.. ?
If I understand you correctly, you want to pass this argument to check_user_type decorator, right? Then you need to nest another function in your decorator, using closure to set variables inside it. Something like
def check_user_type(parameter):
def check_user_type_inner(func):
def wrapped_func(request, *args, **kwargs):
... # parameter here depends on argument passed to most outer function
return func(request,*args, **kwargs)
return wrapped_func
return check_user_type_inner
then parameter is available inside scopes of both inner functions.
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 have code like this in my pyramid project:
class SomeViews(object):
#view_config(...)
def view_a(request):
return {...}
#view_config(...)
def view_b(request):
return {...}
I would like to decorate the view methods to modify the returned dictionary. It's possible to apply an decorator to a view, if it's the first one before view_config. Otherwise Pyramid is still using the original function, due to the nature of Venusian.
Because I would apply the same decorator to all methods in a class, I would prefer to use a class decorator. So instead of doing
#view_config(...)
#my_decorator("some_meta_info")
def view_b(request):
return {...}
for each method, I would like to do
#my_decorator("some_meta_info")
class SomeViews(object):
...
But because the class decorator is executed after the view_config calls, again it does not work with Venusian. I had a look at the view_defaults implementation, to get a hint how to solve my problem, but I did not figured out how it works.
Any hint how to do that? What I want to do, is just to modify the result dictionary of a set of view methods. I also thought about using the BeforeRender event, but I found no way to inject the required meta data in a way that I can access it in the event handler. Using decorators would anyway be the more natural and pythonic way in my opinion.
import functools
def my_decorator(value):
def _dec(f):
#functools.wraps(f)
def wrapper(context, request):
print 'hey look!', value
return f(context, request)
return wrapper
return _dec
#view_defaults(decorator=my_decorator('some meta info'))
class SomeViews(object):
def __init__(self, request):
self.request = request
#view_config(..., renderer='string')
def view_a(self):
return 'foo'
Think of view_defaults as default options passed to every view_config on the class. If you add a decorator to the view_config though, the defaults are overridden and your default decorator would be dropped.