I have a mixin which beside other things simplifies call of request.user object.
class MyMixin(LoginRequiredMixin, View):
...
leader = False
employee = False
def dispatch(self, request, *args, **kwargs):
self.leader = request.user.is_leader()
self.employee = request.user.is_employee()
return super().dispatch(request, *args, **kwargs)
...
And I have a heir of DetailView which has it's own dispatch method.
class MyDetailView(MyMixin, DetailView):
def dispatch(self, request, *args, **kwargs):
if not self.leader:
raise PermissionDenied
return super().dispatch(request, *args, **kwargs)
But as you could've told it ain't working. Is there a way to elegantly call parent dispatch method from it's heir?
You could make your attributes into properties so that the order of execution does not matter. Using cached_property will mean that the property is only evaluated once for each request
from django.utils.functional import cached_property
class MyMixin(LoginRequiredMixin, View):
#cached_property
def leader(self):
return self.request.user.is_leader()
#cached_property
def employee(self):
return self.request.user.is_employee()
Related
Hey I want to add if statement and according to it decide if to delete the object or not.
I could not find it online.
In general how can I add if statements to any CBV including Update for example..
This is my DeleteView func:
class PostDeleteView(LoginRequiredMixin, DeleteView):
model = Post
success_url = reverse_lazy('TheApp:post_list')
EDIT! THE SOLUTION THAT WORKED FOR ME:(Thanks to AKX)
def delete(self, request, *args, **kwargs):
if (Post.author == request.user.username):
return super().delete(request, *args, **kwargs)
else:
return HttpResponse('You are not the owner of this Post! You can not delete it!')
Well, as you know, CBVs' methods map to HTTP methods, so just override delete() and add your condition:
class SomeView(..., DeleteView, ...):
def delete(self, request, *args, **kwargs):
if request.GET.get('really') != 'true':
return HttpResponse('I knew you were just kidding!')
return super().delete(request, *args, **kwargs)
I noticed that I am setting site-wide context variables and request variables for many views on my site. Naturally, this situation calls for inheritance. If all of my view class-based views are inheriting from SiteView instead of the generic View, I can factor out all the commonalities into the SiteView child class. I can then inherit from SiteView on all my views. But, I cannot get this to work. Here is my code:
from django.contrib.auth.decorators import login_required
from django.views.generic import View
from django.utils.decorators import method_decorator
class SiteView(View):
''' Extends the generic django-supplied View class '''
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
return super(SiteView, self).dispatch(*args, **kwargs)
def get(self, *args, **kwargs):
''' Adds the variables required in the get request '''
context = super(SiteView, self).get(*args, **kwargs)
context['common_var'] = 'some common value'
context['user'] = request.user
return self.render_to_response(context)
This throws the following TypeError:
dispatch() missing 1 required positional argument: 'request'
Any help would be appreciated
Edit: Even though the correct answer is marked, there were other issues with the code. In particular, the get method of the SiteView should not have the following line:
context = super(SiteView, self).get(*args, **kwargs)
This is because the View class does NOT have any get method.
You forgot to pass the request to the super().dispatch(..) call:
class SiteView(View):
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
return super(SiteView, self).dispatch(request, *args, **kwargs)
or you can just omit the request in the dispatch parameters, and thus pass it through *args and **kwargs:
class SiteView(View):
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(SiteView, self).dispatch(*args, **kwargs)
It is however probably more elegant, to pass the name of the function, like:
#method_decorator(login_required, name='dispatch')
class SiteView(View):
# ...
EDIT: Note that a View has no get(..), post(..), etc. method. The dispatch(..) method will look if such method exists, and if so redirect to it. If such method does not exists, it will return a "405 Method Not Allowed" response.
Your get(..) function thus be implemented like:
#method_decorator(login_required, name='dispatch')
class SiteView(View):
''' Extends the generic django-supplied View class '''
def render_to_response(self, context):
# ...
def get(self, request, *args, **kwargs):
context = {
'common_var': 'some common value',
'user': request.user
}
return self.render_to_response(context)
It perhaps makes more sense to implement a "mixin" (perhaps with a subclass of the LoginRequiredMixin mixin [Django-doc].
For example like:
class SiteViewMixin(LoginRequiredMixin):
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context.update(common_var='some common value', user=self.request.user)
return context
and then use the mixin in another view, like:
class SomeView(SiteViewMixin, TemplateView):
# ...
'Generic view class' in Django puzzles me very much.
for instance:
class ProfileDetailView(DetailView):
def get_object(self):
username = self.kwargs.get('username')
if username is None:
raise Http404
return get_object_or_404(User, username__iexact=username, is_active=True)
Without the procedure of getting instantiated, it works as well.
What I can understand is:
class ProfileDetailView(DetailView):
def __init__(self, *args, **kwargs)
super().__init__(*args, **kwargs)
def get_object(self):
username = self.kwargs.get('username')
if username is None:
raise Http404
return get_object_or_404(User, username__iexact=username, is_active=True)
What's the mechanism behind it?
That's the fundamental principle of inheritance:
for your reference:
9. Classes — Python 3.6.3 documentation
class Mapping:
def __init__(self, iterable):
self.items_list = []
self.__update(iterable)
def update(self, iterable):
for item in iterable:
self.items_list.append(item)
__update = update # private copy of original update() method
class MappingSubclass(Mapping):
def update(self, keys, values):
# provides new signature for update()
# but does not break __init__()
for item in zip(keys, values):
self.items_list.append(item)
In Python3 super method behaves a bit more magically. It automatically figures out the parent of the class and implicitly passes the self as well. It has nothing to do specifically with Django itself. It's been done so to reduce the code redundancy. It is pretty irritating to be forced to change all super calls when changing the name of the class.
# Python2
class B(A):
def __init__(self, *args, **kwargs):
super(A, self).__init__(*args, **kwargs)
# Python 3
class B(A):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Now as far as Django generic views are concerned, there is an as_view class method that creates a callable instance of the View, which delegates the call to dispatch method.
The __init__ method named constructor call automatically when you instantiated Class. Here As you inherit DetailView so super() call takes all __init__ functionality from DetailView.
class ProfileDetailView(DetailView): # here DetailView is inherited
def __init__(self, *args, **kwargs) # Own class(ProfileDetailView)'s __init__
super().__init__(*args, **kwargs) # it takes DetailView's __init__ behabiour
and it is not ProfileDetailView own so 2nd __init__ avoid self.
I have this CBV:
class GetStuff(View):
def get(self, request, company_id):
...
I want to decorate the get function with a custom function which takes the request and company_id arguments and check some permissions.
Any idea on how to achieve this? Most information I've found about decorators focus on FBV.
This is what I have so far:
def custom_decorator(func_view):
def wrapper(request, company_id):
if not request.user.is_staff:
# do_something()
return func_view(request, company_id)
return wrapper
You have to decorate the dispatch method
To decorate every instance of a class-based view, you need to decorate the class definition itself. To do this you apply the decorator to the dispatch() method of the class. django doc
from django.utils.decorators import method_decorator
class GetStuff(View):
#method_decorator(custom_decorator)
def dispatch(self, *args, **kwargs):
return super(GetStuff, self).dispatch(*args, **kwargs)
or
#method_decorator(custom_decorator, name='dispatch')
class GetStuff(View):
def dispatch(self, *args, **kwargs):
return super(GetStuff, self).dispatch(*args, **kwargs)
you can find other decorating techniques in the docs
I am trying to implement staff_member_required mixins:
Here are the two ways I found on how to do so:
First:
class StaffRequiredMixin(object):
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
if not request.user.is_staff:
messages.error(
request,
'You do not have the permission required to perform the '
'requested operation.')
return redirect(settings.LOGIN_URL)
return super(StaffRequiredMixin, self).dispatch(request,
*args, **kwargs)
Second:
class StaffRequiredMixin(object):
#classmethod
def as_view(self, *args, **kwargs):
view = super(StaffRequiredMixin, self).as_view(*args, **kwargs)
return staff_member_required(view)
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
if not request.user.is_staff:
messages.error(
request,
'You do not have the permission required to perform the '
'requested operation.')
return redirect(settings.LOGIN_URL)
return super(StaffRequiredMixin, self).dispatch(request,
*args, **kwargs)
What I want to know is:
Why the second way is overriding the as_view() method and wrapping it with staff_member_required ?
Do we get any 'additional' advantages by doing so ?
I am new to these mixins. Please help.
TL; DR: they're close to the same, the difference is in checking is_active as well as is_staff and the error messages. You probably don't need both because the as_view override negates the need for the dispatch override anyway.
These are really just two ways of doing close to the same thing.
This code:
class StaffRequiredMixin(object):
#classmethod
def as_view(self, *args, **kwargs):
view = super(StaffRequiredMixin, self).as_view(*args, **kwargs)
return staff_member_required(view)
...could actually be used alone to implement the staff_member_required decorator. In this case the staff_member_required functionality gets called in the view's as_view() function (i.e., from as_view() in your URLConf).
This code:
class StaffRequiredMixin(object):
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
if not request.user.is_staff:
messages.error(
request,
'You do not have the permission required to perform the '
'requested operation.')
return redirect(settings.LOGIN_URL)
return super(StaffRequiredMixin, self).dispatch(request,
*args, **kwargs)
...filters users in the dispatch method. You can see in the Django codebase that as_view actually calls dispatch. This means that if you use both together you won't actually ever trigger the if not request.user.is_staff code in the dispatch method because any user who doesn't pass would have been filtered out in the as_view method.
The second difference is that staff_member_required is slightly different from what the first code does. If you check out the code, you'll notice that staff_member_required also checks whether the user's is_active flag passes (not just is_staff like in your dispatch decorator). It also doesn't pass the messages.error like in your code.