django transaction management with class based view - python

I have a class based view and that has method post as follows
class Comment(View):
def dispatch(self, request, *args, **kwargs):
super(Comment, self).dispatch(request, *args, **kwargs)
#method_decorator(transaction.non_atomic_requests)
def post(self, request, *args, **kwargs):
<Some code>
In above example transaction.non_atomic_requests does not make post method non atomic, but if I use same decorator for dispatch method then it works? Why?
I am using django 1.9

When you use the transaction.non_atomic_requests decorator, it sets an attribute _non_atomic_requests on the decorated object.
Django then checks for the _non_atomic_requests attribute when the view runs.
When Django runs a class based view, it doesn't run the dispatch method directly, it runs the view that is returned by the as_view() method.
There is code in the as_view() method that copies attributes like _non_atomic_requests from the dispatch method to the view that is returned.
However the as_view method does not copy attributes from any other methods like get or post. When you decorate the post method, the _non_atomic_requests attribute is not copied to the view, so the decorator does not work.
Therefore, you must apply decorators like transaction.non_atomic_requests or csrf_exempt to the dispatch method.

According to the django documentation:
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.
The dispatch is the first method that handle the request for the view.
https://docs.djangoproject.com/en/1.11/topics/class-based-views/intro/#decorating-the-class

Related

Where should I call custom method in django CBV? (DetailView)

I made a custom method that save session when call get() method in DetailView page.
Here's sample of my code.
class CustomDetailView(DetailView):
model = Jobs
template_name = "jobs/job_detail.html"
context_object_name = "job"
def custom_save_session(self, request):
# save session and so on
...
I wonder where should I call custom_save_session method.
I tried to call it inside get(), get_context_data() and so on.
All work well, but I have curiosity what is the best method.
Should I call inside get() method? Cause I want to save session data every time get() called.
Is there any method that call custom method when every time get() called in DetailView?
(I also wonder not only DetailView, but also all CBV in django too)
We can call extra methods in class based DetailView in two ways.
By overriding disatch method
By overriding get method
dispach method
class CustomDetailView(DetailView):
model = Jobs
template_name = "jobs/job_detail.html"
context_object_name = "job"
def custom_save_session(self, request):
pass
def dispatch(self, request, *args, **kwargs):
self.custom_save_session(request)
return super(CustomDetailView, self).dispatch(request, *args, **kwargs)
get method
class CustomDetailView(DetailView):
model = Jobs
template_name = "jobs/job_detail.html"
context_object_name = "job"
def custom_save_session(self, request):
pass
def get(self, request, *args, **kwargs):
self.custom_save_session(request)
return super(CustomDetailView, self).get(request, *args, **kwargs)
Both of the above ways is fine. But, I will go with dispatch method because we are dealing with session data so, it would be better.
Every class based view has an order of running things, each with it's own method.
CBV have a dedicated method for each step of execution.
You would call your custom method from the method that runs the step where you want to call your custom method from.
So you have to decide where your custom method should run, and define your own method on top of the view generic method for this step.
More from docs
In the url the CBV returns as_view(), which is callable function that gets a reuqest. From this entry point (actually the CBV dispatch method), the CBV runs all the steps.

Class-based views: where to check for permissions?

I am not very comfortable using class-based views but I am aware of their perks so I am forcing myself to start using them more often.
There's this view that receives a path param: manage/:id to manage a particular entity.
class MyView(TemplateView):
template_name = '...'
def get_context_data(self, **kwargs):
context = super(MyView, self).get_context_data(**kwargs)
context['entity'] = get_object_or_404(Entity, pk=self.args[0])
return context
An Entity includes a list of authorized users to perform special actions. This view, MyView is one of those special actions.
I tried making a decorator for the view but it required finding the Entity first so I wasn't sure how to work that out.
Now, I have a check_permission(request, entity) function that checks if the current user is one of these authorized ones.
My question is where should I call this function in the class-based views like MyView which will be any of these views considered "special actions"?
Should I call it just from get_context_data()?
put it into dispatch(). It could look like this:
class MyView(TemplateView):
template_name = '...'
def dispatch(self, request, *args, **kwargs):
entity = get_object_or_404(Entity, pk=args[0])
if not check_permission(request, entity):
raise Http404
return super(MyView, self).dispatch(request, *args, **kwargs)
You can check permissions in the dispatch as yedpodtrzitko has said. I think it's also a good idea to throw it inside of a mixin that you can put on your views.
Here's an example:
from django.core.exceptions import PermissionDenied
class ViewPermissionsMixin(object):
"""Base class for all custom permission mixins to inherit from"""
def has_permissions(self):
return True
def dispatch(self, request, *args, **kwargs):
if not self.has_permissions():
raise PermissionDenied
return super(ViewPermissionsMixin, self).dispatch(
request, *args, **kwargs)
class MyCustomPermissionMixin(ViewPermissionsMixin):
def has_permissions(self):
# here you will have access to both
# self.get_object() and self.request.user
return self.request.user in self.get_object().special_list_of_people
Now you can throw MyCustomPermissionMixin on your view:
class MyView(MyCustomPermissionMixin, TemplateView):
# ...
In your case, since you're using a TemplateView, you should also make a get_object() method that returns the object that you want to deal with. Template views don't have this method by default.
Finally, just want to say that you will love Django's class based views once you learn some more about how to use them.
Take a look at Django Braces, it's a solid set of mixins which are designed around permissions.
How specifically you deal with permissions depends largely on implementation. I've done it in dispatch() before which is the way Braces does it but if it's specific to an object or queryset, I'll do it in the actual get_object or get_queryset methods as part of a DetailView.
For example if you had a creator associated with an Entity, you could override get_object to check the current logged in user is the Entity's creator.
class EntityView(LoginRequiredMixin, DetailView):
model = Thing
def get_object(self, **kwargs):
return Entity.objects.get_object_or_404(
pk=kwargs['entity_id'],
creator=self.request.user
)
Note: LoginRequiredMixin is a part of Braces. Very slick.

How to call a custom method within generic.view at the end of the request

I have a generic view in Django where I add a custom Mixin. I need the new mixin method has_action to be called at the end or towards the end of the view request cycle.
from django.views.generic import View
class AboutView(MyCustomMix, View):
#edited for brevity
Custom Mixin
def has_action(self, request, view):
# do some stuff
What are my options here to make sure has_action is called? The only way I can think of is to renamehas_action method to something else like def as_view and use super as I know this gets called.
But is there a way I can get has_action to be called somewhere at the end of the request cycle within my view (I still need access to request, view). I've seen this could be done by overriding init on the view but but this gets run at the start of the request cycle.
I think you can override the dispatch method:
def dispatch(self, request, *args, **kwargs):
response = super(AboutView, self).dispatch(request, *args, **kwargs)
self.has_action(request, self)
return response
Not sure what the view parameter to has_action is.

django-haystack - accessing request.GET from subclassed FacetedSearchView class

I have subclassed django-haystack's FacetedSearchView, and want to have an action take place when a specific GET parameter is present in the view's URL. I know exactly how to do this if I had a function-based view, but am at a complete loss when using a subclass of a subclass.
The request variable should be available somehow. I assume I need to call it up from some kind of super() call, maybe in an __init__() override? I've tried the following:
def __init__(self, *args, **kwargs):
super(MySearch, self).__init__(*args, **kwargs)
if self.request.GET.get("date_facet", ""):
do_something()
But it tells me that the returned request object is None, though by the URL it clearly shouldn't be.
Any thoughts?
Turns out, the request variable is accessible, it just has to be through self, which means you can only access it through a function common to the parent class. __init__ wasn't working, because request isn't defined until later in the parent class. To get things to work I ended up overwriting the get_results() class (originally just return self.form.search()), in a way similar to the following:
class Foo(ParentClass):
def get_results(self):
if 'date_facet' in self.request.GET:
year = int(self.request.GET['date_facet'])
return self.form.search().filter(some_filter_function)
return self.form.search()

django login_required decorator outside view

I am trying to use login_required decorator outside django view.
I am using this in a function on my utils.
#login_required
def somefunc():
#logic
and then I am calling this somefunc() in my view
class MyView(View):
def get(self, request, *args, **kwargs):
my_func = Somefunc()
When I do this it says object has no attribute 'user'
Whats the issue here ?
login_required is looking for a request object as the first argument to the decorated view function. The request object has a user attribute, which is then checked to see if the user is actually logged in. You can't wrap an arbitrary function with this decorator and expect it to work exactly the same as with an actual view function.
You should add the login_required decorator on the View. This can either be achieved by overriding the dispatch method.
class MyView(View):
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(MyView, self).dispatch(*args, **kwargs)
You could create your own mixins for achieving this or just use django-braces's LoginRequiredMixin.
If you want to have this check in the function it might be better to raise a PermissionDenied exception in somefunc.
Either way you will need the request/user in somefunc

Categories

Resources