Django views as_view() - django

Example views.py:
from django.http import HttpResponse
from django.views import View
class MyView(View):
def get(self, request, *args, **kwargs):
return HttpResponse('Hello, World!')
Example urls.py:
from django.urls import path
from myapp.views import MyView
urlpatterns = [
path('mine/', MyView.as_view(), name='my-view'),
]
classmethod as_view(**initkwargs)¶
What happens when a request from the user is received?
Is the view returned from ClassView.as_view() called?
Or is an instance of Class View created?

The urls.py will call the callable that is passed to the path(..). Here .as_view() will thus return a function that is called. You can find the source code for this in the GitHub repositiory:
#classonlymethod
def as_view(cls, **initkwargs):
"""Main entry point for a request-response process."""
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that."
% (key, cls.__name__))
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r. as_view "
"only accepts arguments that are already "
"attributes of the class." % (cls.__name__, key))
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.setup(request, *args, **kwargs)
if not hasattr(self, 'request'):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
# take name and docstring from class
update_wrapper(view, cls, updated=())
# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
return view
It will thus return the view function that it constructs in the as_view() method. When the view() method is then called when you "trigger" the view, it will construct a View instance with the optional **initkwargs you passed to the as_view() method. This thus means that each HTTP request, will construct a new View object.
Next it will "setup" the obect by adding the request, the args and the kwargs to the instance. Finally it will call self.dispatch(..) this method will take a look at the request method (GET, POST, PUT, PATCH, DELETE, …), look if it is part of the acceptable methods, and in that case trigger the corresponding .get(..), .post(..), .put(..), … method, and return the result of that method.

Related

Using Django CBV TemplateView with custom render_to_reponse

I'm trying to override the render_to_response method in TemplateView to include an additional parameter for the reponse_class for the HTTP status, but having trouble using:
class ErrorView(TemplateView):
''' Inserts http status code into response '''
status = None
def render_to_response(self, context, **response_kwargs):
if self.status is None:
raise ImproperlyConfigured("ErrorView requires definition of status")
return super(ErrorView,self).render_to_response(context,{'status': self.status})
class Error404View(ErrorView):
template_name = '404.html'
status = 404
The render_to_response method (from TemplateResponseMixin) is defined with three parameters:
def render_to_response(self, context, **response_kwargs)
However, when TemplateView calls it from the get method, it only passes the context:
return self.render_to_response(context)
How can I pass response_kwargs to render_to_response?
Why not create your own ErrorTemplateView and override get method. Since you cannot reuse get method from TemplateView for your use case.
class ErrorTemplateView(TemplateView):
status = None
def get(self, request, *args, **kwargs):
if self.status is None:
raise ImproperlyConfigured("ErrorTemplateView requires definition of status")
context = self.get_context_data(**kwargs)
return self.render_to_response(context, status=self.status)
By the way if you need to customize error views, did you check this from docs?

how to test view with custom decorator relate to user in django?

I have a view that looks like this:
#login_required
#active_required()
def myView(request):
print 'in my view'
The active_required customer decorator looks like this:
def active_required():
def decorator(func):
def inner_decorator(request, *args, **kwargs):
my_user = request.user
if my_user.active:
return func(request, *args, **kwargs)
else:
return HttpResponseRedirect(reverse('activate'))
return wraps(func)(inner_decorator)
return decorator
My test looks like this:
def test_my_view(self):
self.client.login(username='user', password='11111111')
response = self.client.post(reverse('my-view'), data, follow=True)
self.assertEqual(response.status_code, 200)
I get the following error:
#active_required
TypeError: active_required() takes no arguments (1 given)
If the database that is created in the test doesn't contain this user that is active, how do I add them? Or am I receiving this error for another reason?
Well looking at the Django login_reguired source login_required takes as first parameter the function that this one will be decorated.
So I think this code should works:
def active_required(func):
#wraps(func)
def inner_decorator(request, *args, **kwargs):
my_user = request.user
if my_user.active:
return func(request, *args, **kwargs)
else:
return HttpResponseRedirect(reverse('activate'))
return inner_decorator
return active_required
If this code does not work (Has not been tested yet) you can use user_passes_test decorator :
def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
"""
Decorator for views that checks that the user passes the given test,
redirecting to the log-in page if necessary. The test should be a callable
that takes the user object and returns True if the user passes.
"""
def decorator(view_func):
#wraps(view_func, assigned=available_attrs(view_func))
def _wrapped_view(request, *args, **kwargs):
if test_func(request.user):
return view_func(request, *args, **kwargs)
path = request.build_absolute_uri()
# urlparse chokes on lazy objects in Python 3, force to str
resolved_login_url = force_str(
resolve_url(login_url or settings.LOGIN_URL))
# If the login url is the same scheme and net location then just
# use the path as the "next" url.
login_scheme, login_netloc = urlparse(resolved_login_url)[:2]
current_scheme, current_netloc = urlparse(path)[:2]
if ((not login_scheme or login_scheme == current_scheme) and
(not login_netloc or login_netloc == current_netloc)):
path = request.get_full_path()
from django.contrib.auth.views import redirect_to_login
return redirect_to_login(
path, resolved_login_url, redirect_field_name)
return _wrapped_view
return decorator
This one is on the link above and the first parameter this one takes it's a function and that function must accept an parameter that parameter is the user
So doing this I'm sure your code must works:
from django.contrib.auth.decorators import user_passes_test, login_required
#login_required
#user_passes_test(lambda user: user.is_active())
def myView(request):
print 'in my view'

How to write a decorator for a class based view -- permision based on object from view

right now I'm using this app for permission checking: django-rules
However it hasn't been updated for over a year now and there's no decorator for the "new" (since django 1.3) class based views. I would like to be able to use at the urls.py like this:
url(r'^casos/(?P<pk>\d+)/editar/$', rules_permission_required('lawsuits.logical_check', raise_exception=True)(CaseUpdateView.as_view()), name='case_edit'),
I can't figure out how to get the object from the class based view from the decorator. Do you guys have any idea? Here's what I have so far:
from django.utils.decorators import available_attrs
def rules_permission_required(perm, queryset=None, login_url=None, raise_exception=False):
def wrapper(view_func):
#wraps(view_func, assigned=available_attrs(view_func))
def inner(request, *args, **kwargs):
#view_func is the class based view -> <function MyEditView at 0x94e54c4>
print view_func.get_object() # doesnt work
print view_func(request, *args, **kwargs).get_object() # doesnt work either
#any ideas?
if not request.user.has_perm(perm, obj=obj):
return redirect_to_login(request, login_url, raise_exception)
return view_func(request, *args, **kwargs)
return inner
return wrapper
Many thanks in advance!
Use method_decorator on the dispatch() method: https://docs.djangoproject.com/en/dev/topics/class-based-views/#decorating-class-based-views
from django.utils.decorators import method_decorator
class ClassBasedView(View):
#method_decorator(rules_permission_required)
def dispatch(self, *args, **kwargs):
return super(ClassBasedView, self).dispatch(*args, **kwargs)
Or you could decorate the output of the as_view class method, either in your url config (as described in the link above), or by saving the instance into a variable.
class ClassBasedView(View):
def dispatch(self, *args, **kwargs):
return super(ClassBasedView, self).dispatch(*args, **kwargs)
class_based_view = rules_permission_required(ClassBasedView.as_view())
Though I'm not quite sure whether the last example could cause thread safety problems (depends on how Django handles the instances). The best way is probably to stick with the method_decorator.
I ended up using a class decorator
def rules_permission_required(perm, queryset=None, login_url=None, raise_exception=False):
def wrapper(cls):
def view_wrapper(view_func):
#wraps(view_func, assigned=available_attrs(view_func))
def inner(self, request, *args, **kwargs):
# get object
obj = get_object_from_classbased_instance(
self, queryset, request, *args, **kwargs
)
# do anything you want
return inner
cls.dispatch = view_wrapper(cls.dispatch)
return cls
return wrapper

Wrapping/decorating a function in urls.py vs in views.py

So, I'm pretty familiar with wrapping functions in views.py. So I've written a decorator to redirect to the default REDIRECT_URL if the user is logged in (sort of a reverse login_required); it's based on how I've made views in the past:
def not_logged_in(redirect_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
def decorator(view_func, *args, **kwargs):
def wrapper(request, *args, **kwargs):
if not request.user.is_authenticated():
return view_func(*args, **kwargs)
else:
redirect_url = (request.REQUEST.get(redirect_field_name, redirect_url) or
settings.REDIRECT_URL)
return HttpResponseRedirect(redirect_url)
return wrapper
return decorator
However, I get the following error: 'function' object has no attribute 'status_code' which is caused by a MiddleWare expecting an HttpResponse. When I look at the value for response, I see that it's <function wrapper at 0x2b3a9922a500>.
Here's how I'm calling it in urls.py:
url(r'login/',
not_logged_in(auth_views.login),
{'authentication_form': LoginForm },
),
Here’s my implementation of the same thing.
def logout_required(view):
def f(request, *args, **kwargs):
if request.user.is_anonymous():
return view(request, *args, **kwargs)
return HttpResponseRedirect(settings.LOGIN_REDIRECT_URL)
return f
In urls.py:
urlpatterns = patterns("",
url(r"^login/", logout_required(login), {"template_name": "users/login.html"}, "login"),
# ...
I hope this helps (unsure though).
The first argument to a decorator should be the function that needs to be decorated.
def not_logged_in(func, redirect_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
The decorator function is also not needed. Return the wrapper function from not_logged_in.
The way you've implemented your decorator, it is parameterised and therefore callable: that's why you've got the extra level of function that fizixx wrongly says is not required. You need to call the outer wrapper initially, in order to return the actual decorated function. So something like:
url(r'login/',
not_logged_in(auth_views.login)('/redirect/', 'redirect_field'),
{'authentication_form': LoginForm },
),

Django: Staff Decorator

I'm trying to write a "staff only" decorator for Django, but I can't seem to get it to work:
def staff_only(error='Only staff may view this page.'):
def _dec(view_func):
def _view(request, *args, **kwargs):
u = request.user
if u.is_authenticated() and u.is_staff:
return view_func(request, *args, **kwargs)
messages.error(request, error)
return HttpResponseRedirect(request.META.get('HTTP_REFERER', reverse('home')))
_view.__name__ = view_func.__name__
_view.__dict__ = view_func.__dict__
_view.__doc__ = view_func.__doc__
return _view
return _dec
Trying to follow lead from here. I'm getting:
'WSGIRequest' object has no attribute '__name__'
But if I take those 3 lines out, I just get a useless "Internal Server Error". What am I doing wrong here?
This decorator already exists as
from django.contrib.admin.views.decorators import staff_member_required
#staff_member_required
Trunk:
http://code.djangoproject.com/browser/django/trunk/django/contrib/admin/views/decorators.py
For Class Based Views, you can decorate the view class's dispatch method like this:
from django.contrib.admin.views.decorators import staff_member_required
from django.utils.decorators import method_decorator
#method_decorator(staff_member_required, name='dispatch')
class ExampleTemplateView(TemplateView):
...
This style of decorator function is used with a parameterised decorator - eg when you do:
#staffonly(my_arguments)
def function(request):
blah
If you're not actually calling the outer function, ie you're using it like this:
#staffonly
def function(request):
You will get odd results, as the function object will be passed to the wrong one of the nested functions in the decorator.
I use Your Decorator and I face only one error:-
'bool' object is not callable
this error comes from here if u.is_authenticated() and u.is_staff:
I change u.is_authenticated() to u.is_authenticated and it nicely works for me