Going to another view - python

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

Pass multiple arguments to a decorator in Python

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.

Django reqired parameters in url query

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.

Python mixin/decorator/__metaclass__ for base class enhancement

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.

Calling login_required inside decorator

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)

Calling a decorator from another view

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)

Categories