I have the following structure in Django:
class EmailView(View, ABC):
def post(self, request):
pass
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
.........
class Base(AccessMixin, EmailView, ABC):
.....
class ADTView(ABC):
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
.......
class BaseMixin(Base, SubscribeNewsletterView, ADTView, ABC):
def get_context_data(self, *args, **kwargs):
..............
class ItemListView(BaseMixin, ListView):
...............
If EmailView is inherited by Base(as in example) the method get_context_data from ADTView is not called.
If EmailView is not inherited by Base : class Base(AccessMixin, ABC)
the method get_context_data from ADTView is called.
What is in the method it, doesn't matter (even if I get context , not modifying and returning it) the same thing happen.
What I want is the execution of the method order:
AccessMixin, EmailView, ADTView, ListView
I suppose is happen because ListView inherits from View, but in EmailView I used View, because I need as_view. Basically I'm calling the EmailView with an url, using Ajax.
In fact Python MRO use depth first approach,and you can check the MRO by this code
print(ItemListView.__mro__)
Related
I have to write a custom function inside class based view which is like below:
class ExampleView(ListCreateAPIView,
UpdateAPIView,
DestroyAPIView):
queryset = Example.objects.all()
serializer_class = ExampleSerializer
def get(self, request, *args, **kwargs):
/.........some code............./
def random_custom(self, request, *args, **kwargs):
/.........some code............./
Here above I have a random custom function which I need to call from the url. Now I am not sure how to do that. If it was a modelviewset, we can do it easily like this:
path("example/",users.ExampleView.as_view({"get": "random_custom"}),
),
I have done it before in ModelVIewset,ie call custom functions like above but I am not sure how to do that is genericviews like above.
Update
def dispatch(self, request, *args, **kwargs):
if request.method == 'PUT' and kwargs.get('slug'):
return self.custom_function(request,*args,**kwargs)
return super().dispatch(request, *args, **kwargs)
def custom_function(self, request, *args, **kwargs):
instance = self.get_object()
print(instance)
slug = kwargs.get("slug")
print(slug)
print(request.data)
Here I can see that from dispatch, the custom function is called and I can see the print of instance and slug but when I print(request.data) it says the error below:
AttributeError: 'ExampleView' object has no attribute 'data'
I need request.data to perform some logic in the data.
Have you tried putting random_custom() into get() method? It should be executed right after GET method is initialised by client.
class ExampleView(...):
...
def get(self, request, *args, **kwargs):
self.random_custom()
/.........some code............./
def random_custom(self, request, *args, **kwargs):
/.........some code............./
The code which makes the decision about which method on your APIView-derived class to call is in APIView.dispatch(). You can read the source here.
Here's how it works:
Convert the method name (e.g. GET, POST, OPTIONS) to lowercase. Check that the method name is a valid HTTP method. If not, return an error code.
Check if the object defines a lowercase version of the method name. If not, return an error code.
Use getattr() to get that method, and call it.
This means that there are only two ways to redirect the call from get().
Define get() and make it call random_custom(), as #NixonSparrow describes.
Override the dispatch() method to call something else.
Those are the only two ways to do it.
I have a Django application that uses a JSON API as its data source.
Here's a simplified example of use in one of my views.py:
class GroupsList(LoginRequiredMixin):
def get(self, request, **kwargs):
# Get file list and totals
try:
group_list = group_adapter.list() # makes an API call and ALSO populates a meta info class
except APIAccessForbidden:
return HttpResponseRedirect(reverse('logout'))
return render(request, 'groups/index.html', {
# can I make a mixin to add data here gained from the API call?
'group_list': group_list,
})
This line:
The group_adapter.list() call populates some meta information into another class, that's not related to the group_list itself. I'd like to pass that data to the template. Ordinarily I'd use a context_processor, but when the context processor is called, the API call hasn't been made yet. I could manually check the information and add it to the render() method, but then I'd need to do that in dozens of different views.
Potential Solution #1: Create a Mixin For It
Can I use a mixin here that adds this information to context AFTER the view code runs but BEFORE render passes information to the template?
In other words is there a way to do this:
class GroupsList(LoginRequiredMixin, AddMetaInfoToContextMixin):
and then create a mixin something like this?
class AddMetaInfoToContextMixin(ContextMixin):
def get_context_data(self, **kwargs):
# self.request
context = super().get_context_data(**kwargs)
context['global_meta_information'] = get_global_meta_information()
return context
Potential Solution #2: Make an overridden templateview
Commenter Melvyn pointed out that I can potentially subclass TemplateView and override get_context_data(), so would something like this work?
class TemplateViewWithMeta(TemplateView):
def get_context_data(self, *args, **kwargs):
context = super(Home. self).get_context_data(*args, **kwargs)
context['global_meta_information'] = get_global_meta_information()
return context
class GroupsList(LoginRequiredMixin, TemplateViewWithMeta):
[...]
The typical workflow for a Django generic TemplateView is:
get()
get_context_data()
render_to_response()
So in your case keeping with the spirit of generic views, you could do it like this:
from django.views import generic
class BaseRemoteApiView(generic.TemplateView):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.group_list = None
def get(self, request, *args, **kwargs):
try:
self.group_list = group_adapter.list() # makes an API call and ALSO populates a meta info class
except APIAccessForbidden:
return HttpResponseRedirect(reverse('logout'))
return super().get(request, *args, **kwargs)
class RemoteApiContextMixin(generic.base.ContextMixin):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["group_list"] = self.group_list
context["meta_information"] = get_global_meta_information()
return context
class ConcreteRemoteApiView(RemoteApiContextMixin, BaseRemoteApiView):
pass
Of course, you don't have to make 3 classes and can just combine the 3 into one - depends on how mixable you want to be.
I have my serializers code set up as follows:
class ProductSerializer(serializers.ModelSerializer):
"""
* Serializes Products.
"""
def __init__(self, *args, **kwargs):
print kwargs.pop('product_passed')
super(ProductSerializer, self).__init__(*args, **kwargs)
Now, this serializer is being called from a view using the code below:
product_passed = Product.objects.get(pk=pk)
product_serializer = ProductSerializer(product_passed)
What I want to do is call the product_passed from my serializer init method. How can I do this? Right now, my method does not work.
Pass it like this:
product_serializer = ProductSerializer(product_passed, product_passed= product_passed)
And access it from kwargs: kwargs.get('product_passed')
I'm trying to warp my head around Django's new class-based views from 1.3.
I've done a bit of reading:
http://reinout.vanrees.org/weblog/2011/08/24/class-based-views-usage.html
http://www.caktusgroup.com/blog/2011/12/29/class-based-views-django-13/
But the one thing I haven't seen an example of, or how to do is, can several views subclass a common 'parent' class, reusing the data from there? (Pardon my thrashing of the common nomenclature)
An example of what I am trying to do:
class MyParentClass(TemplateView):
def get(self, request, *args, **kwargs):
session_data = request.session
other_variables = foovars
return self.render_to_response(context)
class MyChildClassOne(TemplateView):
template_name = "template_one.htm"
def get(self,request, *args, **kwargs):
resultant_data = foodata
return {'data' : resultant_data }
class MyChildClassTwo(TemplateView):
template_name = "template_two.htm"
def get(self,request, *args, **kwargs):
other_data = foootherdata
return {'data' : other_data }
So that the only difference between the two child classes is the templates they use and the 'data' they return. Both views would also return session_data and other_variables from the parent class, and therefore not repeating "return session_data, other_variables" in every child class.
First, don't override your view's get method. That's dangerous for several reasons I won't go into here.
Anmyway, what you need is the get_context_data method, which returns the context dict which is being passed to the template.
So, both your child views should look something like:
class ChildView(ParentView):
template_name = "foo"
def get_context_data(self, **kwargs):
context = super(ChildView, self).get_context_data(**kwargs)
context.update({
'foodata': 'bardata',
})
return context
But this is pretty much how views work out of the box; why do you think you need to subclass from an additional custom view class?
Here's a possible way: Your parent class will return a variable called data in the context which will be set by the child class.
Example:
class MyParentClass(TemplateView):
def get(self, request, *args, **kwargs):
session_data = request.session
other_variables = foovars
context['data'] = data
return self.render_to_response(context)
class MyChildClassOne(MyParentClass):
template_name = "template_one.htm"
def get(self,request, *args, **kwargs):
data = foodata
return super(MyChildClassOne, self).get(request, args, kwargs)
class MyChildClassTwo(MyParentClass):
template_name = "template_two.htm"
def get(self,request, *args, **kwargs):
data = foootherdata
return super(MyChildClassTwo, self).get(request, args, kwargs)
Both your child classes inherit from MyParentClass, whose get method automatically sets a variable named data into the context. The data values are provided by the child classes. Once done they call the parent's get method to perform the common operations, including rendering.
I'm starting with django (and stackoverflow!)
I've been trying to create a webpage with a form and a list of items. ( Django - Mixing ListView and CreateView). I came up with a solution but I'm not convinced by my code!
I'm using Django mixin BaseCreateView and BaseListView to generate the form and list context data. But because it's views, they call directly render_to_response().
So I overloaded the get() method to manually call both parents methods and extract the context data. Then I called render_to_response() myself.
class FormAndListView(BaseCreateView, BaseListView, TemplateResponseMixin):
def get(self, request, *args, **kwargs):
formView = BaseCreateView.get(self, request, *args, **kwargs) # formView contains a response_class
listView = BaseListView.get(self, request, *args, **kwargs) # listView contains a response_class as well...
formData = formView.context_data['form'] # extract the form from the response_class
listData = listView.context_data['object_list'] # extract the object list from the response_class
return render_to_response('textfrompdf/index.html', {'form' : formData, 'all_PDF' : listData},
context_instance=RequestContext(request))
On one hand I'm not re-writing what is already in the mixin to manage forms and lists of items... On the other hand, django is calculating the whole render_to_response() 3 times!
What would be the clean Django-way to write this page?
class FormAndListView(BaseCreateView, BaseListView, TemplateResponseMixin):
def render_to_response(context, **responsekwargs)
return context
def get(self, request, *args, **kwargs):
context = {}
context.update( BaseCreateView.get(self, request, *args, **kwargs) ) # formView contains a response_class
context.update( BaseListView.get(self, request, *args, **kwargs) ) # listView contains a response_class as well...
return TemplateResponseMixin.render_to_response('textfrompdf/index.html', context,
context_instance=RequestContext(request))