how to use parametrized decorator in python flask - python-decorators

I build a decorator with parameter to limit access to the certain pages
def page_decorator(arg):
"""
Decorate routes to require access to the page.
"""
def inner_decorator(f):
def wrapped(*args, **kwargs):
if arg not in session.get("menus"):
return redirect("/")
response = f(*args, **kwargs)
return response
return wrapped
return inner_decorator
and I call here
#app.route("/receiving-performance",methods=["GET","POST"])
#login_required
#page_decorator(1)
def receiving_performance():
return render_template("01-Dashboards/01-Performance/01-receiving-performance.html")
it works for the first call function, then it returns an error, when I use it for the next function/route
AssertionError: View function mapping is overwriting an existing endpoint function: wrapped
what can be the problem, thanks.

Related

Django rest framework, request object cannot list all method by dir

In any viewset, build_absolute_uri not in dir(request) but in dir(request._request). Why request.build_absolute_uri call request._request.build_absolute_uri ?
class DefaultUrlViewSet(ModelViewSet):
...
def get_queryset(self):
request = self.request
assert 'build_absolute_uri' not in dir(request)
assert 'build_absolute_uri' in dir(request._request)
assert hasattr(request, 'build_absolute_uri')
print('???? why hasattr but cannot list it in dir?')
return super().get_queryset()
build_absolute_uri is a method of standard HTTPRequest and DRF's Request is Wrapper allowing to enhance a standard HttpRequest instance
Request puts its wrapped HTTPRequest in _request attribute.
In the source code of django-rest-framework's Request It has overridden __getattr__ method such that if any attribute does not exist it will try to get it from self._request.
def __getattr__(self, attr):
"""
If an attribute does not exist on this instance, then we also attempt
to proxy it to the underlying HttpRequest object.
"""
try:
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)

Change standard endpoints urls Django Rest Framewrok

I'm using viewsets.ModelViewSet and want to replace the standard endpoints URLs
for example:
instead of creating new snippet with the "standard" endpoint
POST {BAST_URL}/snippet/
I want to replace it with "create" URL and disabling the standard
POST {BAST_URL}/snippet/create/
I able to create a new custom create method but not to
* use "create" in the URL -> ERROR: Cannot use the #action decorator on the following methods, as they are existing routes: create
* Disabling Standart URL from creating a snippet
#action(detail=False, methods=['post'])
def create_snippet(self, request, *args, **kwargs):
return super(SnippettViewSet, self).create(request, *args, **kwargs)
you need to pass an extra argument url_path to the #action decorator like below
#action(detail=False, methods=['post'], url_path='snippet/create', url_name='snippet_create')
def snippet(self, request, *args, **kwargs):
return super(SnippettViewSet, self).create(request, *args, **kwargs)

Is there a way to use inheritance in django viewsets or do I have to rewrite the functions?

I am writting an API in Django Rest Framework. I am using viewsets however regular methods create() and update() do not do it for me and I have to rewrite them.
Suppose that I need to do just one check to test if creation of an instance is legit, do I still have to rewrite the whole create function?
Currently I have this:
class LocationViewSet(viewsets.ModelViewSet):
permission_classes = (IsAuthenticated,)
serializer_class = LocationSerializer
def create(self, request, *args, **kwargs):
user = request.user
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
if user.can_create_location():
serializer.save()
return Response(serializer.data)
else:
raise exceptions.PermissionDenied('You dont have permission to create this.')
Instead of rewritting the whole thing, is it possible to somehow use inheritance and do something like this?
class LocationViewSet(viewsets.ModelViewSet):
permission_classes = (IsAuthenticated,)
serializer_class = LocationSerializer
def create(self, request, *args, **kwargs):
user = request.user
if user.can_create_location():
return super().create(self, request, *args, **kwargs)
else:
raise exceptions.PermissionDenied('You dont have permission to create this.')
The above does not work as I get this error. I feel like this should be possible to do, but I don't know how. Thanks for your help!
AttributeError at /api/0/location
LocationViewSet' object has no attribute 'data'
Request Method: POST
Request URL: http://127.0.0.1:8000/api/0/location
Django Version: 2.1.7
Yes, it does work, and you're doing it almost correctly... as per your error: the function is trying to access LocationViewSet.data as you're passing self in the first argument, change that call to:
super().create(request, *args, **kwargs)
Python standard docs has some info on super() and a link to a more in depth explanation.

Django - Custom decorator to allow only ajax request

I have few views in my project which are called only by ajax and I need to put in a check that the views are processed only if the request is ajax. So I thought of writing a decorator. Upon searching for similar type of decorators I hit upon few but couldn't understand anything.
I make use of class based views and will use this decorator on get/post methods. I did find few snippets but all were written for function based views which made it more difficult for me to understand as I have never worked upon function based views.
I just need to know what is idea behind decorators and how it works.
from functools import wraps
from django.core.exceptions import PermissionDenied
def require_ajax(view):
#wraps(view)
def _wrapped_view(request, *args, **kwargs):
if request.is_ajax():
return view(request, *args, **kwargs)
else:
raise PermissionDenied()
return _wrapped_view
After a google search I've found this:
from django.http import HttpResponseBadRequest
def ajax_required(f):
"""
AJAX request required decorator
use it in your views:
#ajax_required
def my_view(request):
....
"""
def wrap(request, *args, **kwargs):
if not request.is_ajax():
return HttpResponseBadRequest()
return f(request, *args, **kwargs)
wrap.__doc__=f.__doc__
wrap.__name__=f.__name__
return wrap
Didn't tried it, so you have to try it. The essential part is request.is_ajax() which checks if the request is made through AJAX. Check also the docs for more info on is_ajax() method.
EDIT
To decorate a view class in django see Decorating the class in the documentation. Basically the decorator function wraps a method of the class. So you can use the django #method_decorator() to wrap a method in your decorator function (ajax_required):
#method_decorator(ajax_required)
def method_you_want_to_get_only_AJAX_requests():
......

login_required decorator on ajax views to return 401 instead of 302

While writing some views to respond to ajax requests i find it somewhat strange that the login_required decorator always returns a 302 status code for not authenticated users. As these views are ajax views, this seems somewhat inappropriate. I do not want the user to log in in such a case, but i want Django to tell the client that authentication is required to access such a view (a 401 should be the right status code, i think).
To achieve this, i started to write my own decorator login_required_ajax, but somehow this is beyond my skills. This is what i have come up with so far:
def login_required_ajax(function=None,redirect_field_name=None):
"""
Just make sure the user is authenticated to access a certain ajax view
Otherwise return a HttpResponse 401 - authentication required
instead of the 302 redirect of the original Django decorator
"""
def _decorator(view_func):
def _wrapped_view(request, *args, **kwargs):
if request.user.is_authenticated():
return view_func(request, *args, **kwargs)
else:
return HttpResponse(status=401)
if function is None:
return _decorator
else:
return _decorator(function)
When using this decorator on a view, i get a ViewDoesNotExist exception as soon as i try to access any page on the site.
I first thought that the problem could be the direct return of an HttpResponse when a user is not authenticated, because a response object is not a callable. But then the decorator should work as long as i do not try to access the view in question, shouldn't it? And if this really is the crux, how can i write a decorator that returns a HttpResponse with a status code of 401?
That's a pretty good attempt. Here's a couple of problems I spotted:
Your _decorator function should return _wrapped_view.
The indentation for your if function is None block is a bit off -- the login_required_ajax function needs to return the decorated function.
Here's the decorator with those changes made:
def login_required_ajax(function=None,redirect_field_name=None):
"""
Just make sure the user is authenticated to access a certain ajax view
Otherwise return a HttpResponse 401 - authentication required
instead of the 302 redirect of the original Django decorator
"""
def _decorator(view_func):
def _wrapped_view(request, *args, **kwargs):
if request.user.is_authenticated():
return view_func(request, *args, **kwargs)
else:
return HttpResponse(status=401)
return _wrapped_view
if function is None:
return _decorator
else:
return _decorator(function)

Resources