class PublisherDetail(DetailView):
model = Publisher
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super().get_context_data(**kwargs)
# Add in a QuerySet of all the books
context['book_list'] = Book.objects.all()
return context
According to the basic Python inheritance rules, super().get_context_data(...) would be DetailView.get_context_data(), but since it's not defined, it's inherited down from SingleObjectMixin.
Related
I have several "Listing" views which are very similar and I feel like I'm unecessarily repeating myself. Subclassing seems like the answer, but I've run into problems down the line before when subclassing things in Django so I'd like to ask before doing so.
If I have 2 views, like this, which use the same template, a variable message and different querysets:
class MyGiverView(LoginRequiredMixin, TemplateView):
template_name = "generic_listings.html"
message = ""
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["list_of_stuff"] = MyModel.objects.filter(
giver=self.request.user,
status=1,
)
context["message"] = self.message
return context
class MyTakerView(LoginRequiredMixin, TemplateView):
template_name = "generic_listings.html" # this hasn't changed
message = ""
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["list_of_stuff"] = MyModel.objects.filter(
taker=self.request.user, # this has changed
status__in=(1,2,3), # this has changed
)
context["message"] = self.message # this hasn't changed
return context
Am I going to screw it up by creating a base class like:
class MyBaseView(LoginRequiredMixin, TemplateView):
template_name = "generic_listings.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["list_of_stuff"] = self.qs
context["message"] = self.message
return context
And using it in my views as such:
class MyGiverView(MyBaseView):
qs = MyModel.objects.filter(
giver=self.request.user,
status=1,
)
message = ""
class MyTakerView(MyBaseView):
qs = MyModel.objects.filter(
taker=self.request.user,
status__in=(1,2,3),
)
message = ""
It's DRYer, but I'm unsure of the implications regarding what goes on "under the hood".
Instead of creating a base class, it is better to create a mixin class for what you need.
Also, using ListView would be better for what you need.
This is my suggestion:
class InjectMessageMixin:
message = ""
# Always use this method to get the message. This allows user
# to override the message in case it is needed to do on a request
# basis (more dynamic) instead of using the class variable "message".
def get_message(self):
return self.message
def get_context_data(self, **kwargs):
# This line is super important so you can normally use
# this mixin with other CBV classes.
context = super().get_context_data(**kwargs)
context.update({'message': self.get_message()})
return context
class MyGiverView(LoginRequiredMixin, InjectMessageMixin, ListView):
# Even though this doesn't change I would keep it repeated. Normally,
# templates are not reused between views so I wouldn't say it's necessary
# to extract this piece of code.
template_name = "generic_listings.html"
message = "giver message"
def get_queryset(self):
return MyModel.objects.filter(
giver=self.request.user,
status=1,
)
class MyTakerView(LoginRequiredMixin, InjectMessageMixin, ListView):
# Even though this doesn't change I would keep it repeated. Normally,
# templates are not reused between views so I wouldn't say it's necessary
# to extract this piece of code.
template_name = "generic_listings.html"
message = "taker message"
def get_queryset(self, **kwargs):
return MyModel.objects.filter(
taker=self.request.user, # this has changed
status__in=(1,2,3), # this has changed
)
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 need to write a DetailView in Django. I achieved this functionality. However, I need to add some more data along with the context object. How will I achieve this.
My generic view is:
class AppDetailsView(generic.DetailView):
model = Application
template_name = 'appstore/pages/app.html'
context_object_name = 'app'
I need to add one more variable to the context object:
response = list_categories(storeId)
How about using get_context_data
class AppDetailsView(generic.DetailView):
model = Application
def get_context_data(self, **kwargs):
context = super(AppDetailsView, self).get_context_data(**kwargs)
context['categories'] = list_categories(storeId)
return context
Exactly what the title says. I have a mixin that needs to pull in the id of a model field in order to be useful. I assume the easy way to do that would be to pull it from the URL.
class StatsMixin(ContextMixin):
def get_stats_list(self, **kwargs):
# the ??? is the problem.
return Stats.objects.filter(id=???).select_related('url')
def get_context_data(self, **kwargs):
kwargs['stats'] = self.get_stats_list()[0]
print kwargs
return super(StatsMixin, self).get_context_data(**kwargs)
Here's the view implementation for reference.
class ResourceDetail(generic.DetailView, StatsMixin):
model = Submissions
template_name = 'url_list.html'
queryset = Rating.objects.all()
queryset = queryset.select_related('url')
You can access URL parameters in Django by using, self.args and self.kwargs.
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.