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.
Related
I created a view that manages the products which belong to the current log-in user (so the current user can watch its own products):
class ProductList(View):
def get(self, request, *args, **kwargs):
products = Product.objects.get(user=self.request.user)
#...
I would like to subclass another view from it, which manages the products that belong to a user specified as a GET parameter in the url (probably different to the current user). Is it possible to do it with super() and overriding the method in product_list? Something like this:
class ProductListFromUser(ProductList):
def get(self, request, *args, **kwargs):
#not sure what code if necessary to put here...
super().get(self, request, *args, **kwargs):
#or here
#...
Basically, how can I override the get method from product_list , changing only "self.request.user" by the parameter taken from the URL
You shouldn't do either of these.
If you have a view that renders a list of items, you should use a subclass of ListView. In that view, you can define the product list in the get_queryset method, which you can define in the relevant way in your two different views.
class ProductList(ListView):
def get_queryset(self, *args, **kwargs):
return Product.objects.filter(user=self.request.user)
class ProductListFromUser(ListView):
def get_queryset(self, *args, **kwargs):
return Product.objects.filter(user__username=self.request.GET['user'])
You should rarely need to define get (or post) directly in a class-based view.
I would move the retrieval of the appropriate QuerySet to an extra method which you can override in order to reuse as much of the initial code as possible:
class ProductList(View):
def get_products(self):
return Product.objects.all()
def get(self, request, *args, **kwargs):
products = self.get_products()
# ...
class ProductList(View):
def get_products(self):
qs = super().get_products()
qs = qs.filter(user=self.request.user)
return qs
# no get needed
This very functionality comes with django's ListView and its get_queryset method.
If you retrieve the queryset for a given request using the get_queryset method, that CBV such as DetailView use, then you can simply override the get_queryset of the parent-class.
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
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.
I'm trying to write use Django FormView and a bit of ingenuity to create a view which will allow me to get inputs from a user that will be fed to a function. I'd like the code to be reusable, so I'd like to make a view that will be able to take a target function as a parameter and automagically create a form appropriate to that function. There is more plumbing to be done, but the general idea would be:
class FormViewForFunction(FormView):
template_name = '...'
func = None
def get_form_class(self):
class _FunctionForm(forms.Form):
pass
a = inspect.getargspec(self.func)
for argname in a['args']:
setattr(_FunctionForm, argname, forms.CharField())
return _FunctionForm
The idea would be that then you could set up something in your URLConf that used FormViewForFunction.as_view(func=***insert any function you want***) and you would wind up being presented with a form that was appropriate for specifying parameters for that function. Let's not worry about what would happen on form submission. For now I'm just stuck getting the form to generate properly.
With the code above, the form doesn't wind up having any fields! What am I doing wrong?
form's fields are initialized during initialization, you should override the __init__ method and then append the fields to the self.fields dictionary
This should work:
class FormViewForFunction(FormView):
template_name = '...'
func = None
def get_form_class(self):
a = inspect.getargspec(self.func)
class _FunctionForm(forms.Form):
def __init__(self, *args, **kwargs):
super(_FunctionForm, self).__init__(*args, **kwargs)
for argname in a['args']:
self.fields[argname] = forms.CharField()
return _FunctionForm
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.