I have a decorator which does the same query as the function it's attached to. Is there a way to just pass the Queryset from the decorator so I don't have to run the query twice?
decorator.py
def is_wifi_author(func):
def wrapper(request, wifi_id, **kwargs):
wifi = get_object_or_404(Wifi, pk=wifi_id) # Queryset
# Is this correct?
if request.user != wifi.author:
return redirect('youshallnotpass')
return func(request, wifi_id, **kwargs)
return wrapper
views.py
#is_wifi_author
def edit(request, wifi_id):
# print(request)
wifi = get_object_or_404(Wifi, pk=wifi_id) # Same queryset
# The rest of the view
return render(request, 'app/template.html')
Yes, just checking if the user has access to edit the post. Any comments welcome.
Yes, it's possible by using kwargs:
def is_wifi_author(func):
def wrapper(request, wifi_id, **kwargs):
wifi = get_object_or_404(Wifi, pk=wifi_id) # Queryset
# Is this correct?
if request.user != wifi.author:
return redirect('youshallnotpass')
return func(request, wifi_id, wiki=wiki, **kwargs)
return wrapper
#is_wifi_author
def edit(request, wifi_id, wiki=None):
"""wiki argument is gonna be updated by the is_wifi_author decorator"""
print('Yeii, a wiki', wiki)
# The rest of the view
return render(request, 'app/template.html')
Though I think #Ubaid answer mentions a valid point.
Why use a decorator when you can simply do
wifi = get_object_or_404(Wifi, pk=wifi_id, author=request.user)
Related
I am trying to eliminate redundant code by creating a decorator to handle simple logic that is always repeated. Basically every view I create has the following logic to check if a user is in a class.
#login_required
def view(request, class_id):
class_ = UserClasses.objects.get(user=request.user, class_id=class_id)
# if the user is in the class
if class_:
I'd like to do the following:
View:
#user_passes_test(in_class(request.user, class_id))
#login_required
def view(request, class_id):
Decorator:
from apps.classes.models import UserClasses
def in_class(request, class_id):
class_ = UserClasses.objects.get(user=request.user, class_id=class_id)
if class_:
return true
else:
return false
What's the best way to go about achieving this?
Your decorator should be like this
def user_passes_test(old_fuction):
def new_function(request, class_id, *args, **kwargs):
try:
class_ = UserClasses.objects.get(user=request.user, class_id=class_id)
except Exception as e:
return HttpResponse('ERROR: User not present in the class')
return old_fuction(request, class_id, *args, **kwargs)
return new_function
If the UserClasses contains row with both user and class_id(assumes that user is unique), the view function will be executed.Otherwise it will return an Error response(ERROR: User not present in the class).
And you view function should be
#user_passes_test
#login_required
def view(request, class_id):
if you want the class_ object in the view function, you can do it by simple changes. modify your decorator like
def user_passes_test(old_fuction):
def new_function(request, class_id, *args, **kwargs):
try:
class_ = UserClasses.objects.get(user=request.user, class_id=class_id)
except Exception as e:
return HttpResponse('ERROR: User not present in the class')
return old_fuction(request, class_id, class_, *args, **kwargs)
return new_function
And the view function should be
#user_passes_test
#login_required
def view(request, class_id, class_obj):
where class_obj contains the class_ object
I want to make a decorators which prevents the user to see others profile but can see own profile. If the user has id passed in url as http://example.com/user/?id=5 i want to get the value id=5 in my django decorator. How can I get it any example ?
def admin_or_ownership_required(owner):
def check_admin_or_owner(user):
# pk = request.GET.get('pk', None)
# if pk is not None and pk == user.id:
# return True
if owner == 'Account':
if user.is_superuser or (user.is_active and (user.role == 'admin' or user.role == 'owner')):
return True
else:
return False
elif owner == 'User':
if user.is_superuser or (user.is_active and (user.role == 'admin' or user.role == 'owner')):
return True
else:
return False
else:
return False
return user_passes_test(check_admin_or_owner)
this is my view code
class AccountDetailView(DetailView):
template_name = 'api/account_detail.html'
model = Account
#method_decorator(admin_or_ownership_required('Account'))
def dispatch(self, *args, **kwargs):
return super(AccountDetailView, self).dispatch(*args, **kwargs)
How can I use the request argument in admin_or_ownershipl_required decorator
def check_login(method):
#functools.wraps(method)
def wrapper(request, *args, **kwargs):
if request.GET['id'] == request.user.id
# Give your redirect url
return method(request, *args, **kwargs)
return wrapper
You could use a mixin for your View, where you limit your case queryset, something like:
class MixinRestrictedAccount(object):
def get_queryset(self):
return Account.objects.filter(id=self.request.user.id)
class AccountDetailView(MixinRestrictedBox, DetailView):
[..]
Try to dump the value of kwargs, from there you can access the url parameters. for example :
if you have Url like :
/users/(?P<username>\w+)/$
you can access that value by kwargs parameter , that is passed in your decorator method. You can access that by:
kwargs.get("username")
If you want to access url parameters by GET method, then you can try like this :
request.GET.get("username")
for URL's like :
/users/?username=dominic
Thanks .
I have code that read like this to check if POST parameters are included on the request:
def login(request):
required_params = frozenset(('email', 'password'))
if required_params <= frozenset(request.POST):
# 'email' and 'password' are included in the POST request
# continue as normal
pass
else:
return HttpResponseBadRequest()
When the list of required POST parameters is big, this code gets messy. What I would like to do is something like:
#required_POST_params('email', 'password')
def login(request):
# 'email' and 'password' are here always!
pass
Then I'm confident that both 'email' and 'password' POST parameters are included in the request, because if not, the request would automatically return HttpResponseBadRequest().
Is there a way that Django allows me to do this, and if it doesn't, how can I do it by myself with a decorator?
You would need a custom decorator, but you can take require_http_methods as a base example:
def require_post_params(params):
def decorator(func):
#wraps(func, assigned=available_attrs(func))
def inner(request, *args, **kwargs):
if not all(param in request.POST for param in params):
return HttpResponseBadRequest()
return func(request, *args, **kwargs)
return inner
return decorator
Example usage:
#require_post_params(params=['email', 'password'])
def login(request):
# 'email' and 'password' are here always!
pass
FYI, require_http_methods source code.
i'm sharing my solution;
__author__ = 'yagmurs'
from copy import deepcopy
from rest_framework import status
from rest_framework.response import Response
def require_params(*params):
def decorator(fn):
def wrapped_function(request, *args, **kwargs):
"""
Decorator for django rest service to meet both GET and POST request
"""
error = deepcopy(REQUEST_INVALID_400)
is_param_missing = False
for param in params:
if not get_param_from_request(param, request):
error['result_message'] += param + ", "
is_param_missing = True
if is_param_missing:
error['result_message'] = error['result_message'][:-2]
return Response(error, status=status.HTTP_400_BAD_REQUEST)
else:
return fn(request, *args, **kwargs)
return wrapped_function
return decorator
def get_param_from_request(param, request):
if request.method == 'POST':
return request.data.get(param)
else:
return request.query_params.get(param)
Try with this.
Instead of require.POST(), try with require.POST('email', 'password').
I have written two very simple decorators for my django app :
def login_required_json(f):
def inner(request, *args, **kwargs):
#this check the session if userid key exist, if not it will redirect to login page
if not request.user.is_authenticated():
result=dict()
result["success"]=False
result["message"]="The user is not authenticated"
return HttpResponse(content=simplejson.dumps(result),mimetype="application/json")
else:
return f(request, *args, **kwargs)
def catch_404_json(f):
def inner(*args,**kwargs):
try:
return f(*args, **kwargs)
except Http404:
result=dict()
result["success"]=False
result["message"]="The some of the resources throw 404"
return HttpResponse(content=simplejson.dumps(result),mimetype="application/json")
But when I apply them to my views I get "ViewDoesNotExist" error in the template, saying it could not import the view because it is not callable. What am i doing wrong?
def login_required_json(f):
def inner(request, *args, **kwargs):
#this check the session if userid key exist, if not it will redirect to login page
if not request.user.is_authenticated():
result=dict()
result["success"]=False
result["message"]="The user is not authenticated"
return HttpResponse(content=simplejson.dumps(result),mimetype="application/json")
else:
return f(request, *args, **kwargs)
return inner # <--- Here
Your decorator is returning None, instead of the actual view.
So return the inner function as I've demonstrated above.
I have view:
#decorator
def func(request):
hello = "hello"
return render_to_responce("test.html", locals() )
and template test.html:
{{ hello }}
{{ username }}
I want to write decorator for func(request), which adds a variable, USERNAME, to the function and returns two parameters in the template. I tried to make it as follows:
def decorator(func):
def wrapper( request, *args, **kwargs):
username = request.user.username
q = func(request, *args, **kwargs)
#what I need add here I do not know ...
return q
return wrapper
If your view looks like this:
def func(request, username):
hello = "hello"
return render_to_responce("test.html", locals() )
you can write a decorator like that:
from functools import wraps
def pass_username(view):
#wraps(view)
def wrapper(request, *args, **kwargs):
return view(request, request.user.username, *args, **kwargs)
return wrapper
and then use it like that:
#pass_username
def func(request, username):
hello = "hello"
return render_to_response("test.html", locals())
(just make sure you will treat it in urls.py as if it was def func(request):, without username - this argument will be provided by the decorator)
But indeed this is very simple case that does not really require separate decorator (anyway, it is only one additional line in the view definition).