Say I have the following view:
def show(request):
protect(request)
... some more code here...
return render_to_response
...
"protect" is another app view which I am importing like this: from watch.actions import protect
In protect, I make some checks and if a condition is met, I want to use render_to_response right from "protect" and prevent returning to show. If the condition is not met, I want to normally return to "show" and continue the code execution.
How may I do that?
Thanks.
If its only purpose is what you've described, you should consider writing protect as a view decorator. This answer provides one example of how to do so.
Based on view decorators that I have written, your protect decorator could look something like:
from functools import wraps
from django.utils.decorators import available_attrs
def protect(func):
#wraps(func, assigned=available_attrs(func))
def inner(request, *args, **kwargs):
if some_condition:
return render_to_response('protected_template')
return func(request, *args, **kwargs)
return inner
Which would allow you to then use it like:
#protect
def show(request):
...
return render_to_response(...)
Related
I am trying to build a flask app which will be having RBAC feature. For this I have written a decorator which is working fine but it can only take one argument meaning only one access level(e.g WRITE, READ, admin etc), but I want to pass multiple arguments to it. I have tried passing a list but its not taking it. I have never worked with decorators so need help with it. Thanks.
def permission_required(permission):
def decorator(f):
#wraps(f)
def decorated_function(*args, **kwargs):
if not current_user.can(permission):
abort(403)
return f(*args, **kwargs)
return decorated_function
return decorator
def admin_required(f):
return permission_required(Permission.ADMIN)(f)
I as passing it like this:
#role_needed(Permission.VIEW), but I want to have this #role_needed(Permission.VIEW, Permission.WRITE)
My permission class is like this:
class Permission:
VIEW = 'Vew'
WRITE = 'Write'
ADMIN = 'admin'
First, I'd advise that you have a look at some tutorial on decorators, they are pretty cool and you definitely need to understand the basics if you want to use flask. I personally quite like this RealPython tutorial.
Second, you have two solutions : either default second argument or argument packing.
def permission_required(permission1, permission2=None):
...
or
def permission_required(*perms):
...
I personaly way prefer the second option.
Example:
def permission_required(*perms):
def decorator(f):
#wraps(f)
def decorated_function(*args, **kwargs):
for perm in perms:
if not current_user.can(perm):
abort(403)
return f(*args, **kwargs)
return decorated_function
return decorator
I think you missed the point that decorators are just usual functions, taking a function in argument and another one, the later being by design a wrapper around the original one. In your case, permission_required is a decorator factory, that can be used to specialize a decorator based on input arguments. So all you need to do is to allow passing any number of arguments to your decorator factory:
def role_needed(*permissions):
def decorator(f):
#wraps(f)
def decorated_function(*args, **kwargs):
nonlocal permissions # Just to make sure `permission` is available in this scope
# Implement here how to deal with permissions
return f(*args, **kwargs)
return decorated_function
return decorator
which can be called as intended:
#role_needed(Permission.VIEW, Permission.WRITE, ...)
In the function, permissions will store the input Permission as a Python tuple object.
Is there any chances to specify which parameters are required in url query and automatically pass them into view function?
In urls.py I would like to have something like this:
path('get_part_info?<part>', views.get_part_info, name='get_part_info'),
And in views.py to have something like this:
def get_part_info(request, part):
# do something with part
return JsonResponse({'result': part})
Idea is to avoid ugly construction like: part= request.GET.get('part')
URL path is not a solution, because "part" value can have various extra characters like slashes etc.
You can write a decorator:
from functools import wraps
from django.http import HttpResponseBadRequest, JsonResponse
def query_params(*param_names):
def decorator(func):
#wraps(func)
def inner(request, *args, **kwargs):
try:
params = {name: request.GET[name] for name in param_names}
except KeyError:
return HttpResponseBadRequest("Missing Parameter")
kwargs.update(params)
return func(request, *args, **kwargs)
return inner
return decorator
#query_params("part")
def get_part_info(request, part):
# do something with part
return JsonResponse({"result": part})
This decorator returns a 400 if a parameter is missing, but that could be changed any way you want, for example, redirect to another URL or to use default values.
I am implementing a content-aware caching system for a Django REST API. I would like to develop a component which can be added to existing views that would modify the behavior of the base class by checking the cache and falling back to the base class behavior on a miss.
basically, I have something like this:
class Base:
def get(self, request, *args, **kwargs):
....
return Response
class AnotherBase:
def get(self, request, *args, **kwargs):
....
return Response
class Derived(Base):
pass
class OtherDerived(AnotherBase):
pass
and my initial thought was to do something along the lines of
class Cacheable:
def get(self, request, *args, **kwargs):
cache_key = self.get_cache_key(request)
base_get = #.... and this is the problem
return cache.get(cache_key, base_get(request, *args, **kwargs))
def get_cache_key(self, request):
# .... do stuff
class Derived(Cacheable, Base):
pass
class AnotherDerived(Cacheable, AnotherBase):
pass
So clearly this doesn't work, as I don't know how, or if it's possible, or if it's advisable to access the sibling superclass(es) from a mixin.
My goal is an implementation that allows me to add caching behavior to existing views without touching the internals of the existing classes.
Given a view class, C, s.t. C.get(request, *args, **kwargs) -> Response, is there a function, F, s.t. F(C).get(... does the cache check before falling back to C.get? And in this quasi-formal notation, we'll say that adding a mixin to the leftmost parent class in the class definition counts as a function.
Is it more appropriate to use method decorators? or how would a class decorator work?
And then I've seen references to __metaclass__ in researching this, but I'm not clear on what that approach looks like.
This is Python 3.6
Simple example:
def Base:
def _get_data(self):
# get the data eg from database
return self._get_data_native()
def get(self, request, *args, **kwargs):
return Response(self._get_data())
def Cacheable(Base):
def _get_data(self):
# get from cache ...
result = ...
if result is None:
# or from base ...
result = ...
return result
def Derived(Cacheable):
def _get_data_native(self):
# get the data eg from database
...
By inheriting from Cacheable, you include the caching here, because _get_data is overwritten there.
For this problem, you don't need metaclasses or decorators, if you want to just add caching at one place.
Of course, a decorator could be used for including caching in an even more generic way.
See for example this answer: Is there a decorator to simply cache function return values?
The answer was a decorator and some Django-specific libraries.
from django.utils.decorators import method_decorator
from django.core.cache import cache
def cached_get(cache_key_func=None):
"""
Decorator to be applied via django.utils.decorators.method_decorator
Implements content-aware cache fetching by decorating the "get" method
on a django View
:param cache_key_func: a function of fn(request, *args, **kwargs) --> String
which determines the cache key for the request
"""
def decorator(func):
def cached_func(request, *args, **kwargs):
assert cache_key_func is not None, "cache_key_function is required"
key = cache_key_func(request, *args, **kwargs)
result = cache.get(key)
if result is None:
return func(request, *args, **kwargs)
return Response(result)
return cached_func
return decorator
#method_decorator(cached_get(cache_key_func=get_cache_key), name="get")
class SomeView(BaseView):
...
def get_cache_key(request):
# do arbitrary processing on request, the following is the naïve melody
key = urllib.urlencode(request.query_params)
return key
So the solution is to use Django's built-in method_decorator which applies its first argument, a decorator, to the decorated class's method, named by the second argument, name, to method_decorator. I define a higher-order function, cached_get, which takes another function as its argument, and returns a curried function (closure, so called). By calling this, with the function get_cache_key (and not, mind you, invoking that function) I have a decorator that will be applied to the 'get' method on SomeView.
The decorator itself is a straightforward Python decorator -- in this application, it is cached_func and the original, undecorated get method is func. Thus, cached_func replaces SomeView.get, so when SomeView.get is called, it first checks the cache, but falls back to the undecorated method on a miss.
I'm hopeful this approach provides a balance of generic applicability with content-aware key derivation.
My two cents:
You're walking into obscure territory here. Get familiar with all the related concepts, try a few, then decide.
Here is a good tutorial about metaclasses.
Here there's one about decorators.
I'm in no way affiliated to that site.
I'm trying to write a custom decorator, which will do some checks to see if a user has permission to access a page, but prior to that, the user needs to be authenticated. I thought of using Django's login_required decorator, and then doing my custom logic, however I can't seem to find any way to call the login_required decorator inside my own.
I do know that there are alternatives, like decorating my view like this:
#login_required
#my_custom_decorator
def my_view(request):
pass
Or checking for user.is_authenticated() inside my decorator:
def my_custom_decorator(view_func):
#wraps(view_func)
def wrapper(request, *args, **kwargs):
if not request.user.is_authenticated():
redirect(...)
However I'd like to user Django's logic from login_required.
Is there any way to call a decorator inside a decorator, or any other way to implement my logic without using 2 separate decorators?
Your decorator returns a function, e.g.
def my_custom_decorator(view_func):
#wraps(view_func)
def wrapper(request, *args, **kwargs):
...
return wrapper
You can wrap that function in login_required before you return it:
def my_custom_decorator(view_func):
#wraps(view_func)
def wrapper(request, *args, **kwargs):
...
return login_required(wrapper)
I have a decorator defined in users/views.py and I want to use the same in profile/views.py in django
Decorator function in users/views.py
def is_active_consult(f):
def wrap(request, *args, **kwargs):
try:
usrid = request.session['id']
user = CustomUser.objects.get(id=usrid)
usercons = Userconsultation.objects.get(doctor=user.doctor,status='InProgress')
except ObjectDoesNotExist:
usercons = ''
if usercons:
url = '/encounter_notes/'+str(usercons.userconsultationid)
return HttpResponseRedirect(url)
else:
return f(request, *args, **kwargs)
return wrap
When I try to import like
from users.views import is_active_consult
It gives an import error "cannot import name is_active_consult"
Is it right to define a decorator in a view, if not where do I put it and for now how do I fix the issue.
Regards
A decorator is a function like any other so you can define it wherever you want to, the problems is not the decorator but the import itself: import loop, incorrect path or not set, etc.
1) Did you succeed in importing anything from users/views.py to profile/views.py ?
2) Can you paste your project structure ? ( # ls -R)