Django class-based-view access context data from get_queryset - python

class ProfileContextMixin(generic_base.ContextMixin, generic_view.View):
def get_context_data(self, **kwargs):
context = super(ProfileContextMixin, self).get_context_data(**kwargs)
profile = get_object_or_404(Profile, user__username=self.request.user)
context['profile'] = profile
return context
class CourseListView(ProfileContextMixin, generic_view.ListView):
model = Course
template_name = 'course_list.html'
object_list = None
def get_queryset(self):
profile = self.get_context_data()['profile']
return super(CourseListView, self).get_queryset().filter(creator=profile)
I have the following two class-based-views. CourseListView inherits ProfileContextMixin which I wrote so that I don't have to repeat overriding get_context_data to get the profile every time in my other views.
Now in my CourseListView, I need to filter the result based on the creator argument, which is the same one retrieved in get_context_data
I know my get_queryset works, and it will call get_context_data() to get the profile, but this will also cause my get_context_data to be called twice, executing the same SQL two times.
Is there a way I can access the context efficiently?
UPDATE:
After reading ListView method flowchart, I ended up doing this, but not sure if it's the best way. Feedback is appreciated.
object_list = []
context = None
def get_context_data(self, **kwargs):
return self.context
def get_queryset(self):
self.context = super(CourseListView, self).get_context_data()
profile = self.context['profile']
queryset = super(CourseListView, self).get_queryset()
queryset = queryset.filter(creator=profile)
self.context['object_list'] = queryset
return queryset

You can move getting profile out of get_context_data to upper function, like dispatch, or use cached_property decorator. This way your profile will be stored in _profile argument of view and you will not do second get to DB after calling self.profile second time.
from django.utils.functional import cached_property
class ProfileContextMixin(generic_base.ContextMixin, generic_view.View):
#cached_property
def profile(self):
return get_object_or_404(Profile, user__username=self.request.user)
def get_context_data(self, **kwargs):
context = super(ProfileContextMixin, self).get_context_data(**kwargs)
context['profile'] = self.profile
return context
class CourseListView(ProfileContextMixin, generic_view.ListView):
model = Course
template_name = 'course_list.html'
object_list = None
def get_queryset(self):
return super(CourseListView, self).get_queryset().filter(creator=self.profile)

Related

List view data class based VIEW confuse

class CoursesListView(View):
template_name = 'courses/courses_list.html'
queryset = CoursersModels.objects.all()
# def get_queryset(self):
# return self.queryset
def get(self,request, *args, **kwargs):
queryset = CoursersModels.objects.all()
context = {
#"object_list": self.queryset #If self.queryset is called, the page will not be updated after refresh
"object_list": queryset#Refresh normally after calling
}
return render(request,self.template_name,context)
I am very confused about this code,When I insert a piece of data into the database,If "object_list": self.queryset is called, the page will not be updated after refresh,You must restart django to update the display normally,but when I run "object_list": queryset everything is normal . Thanks in advance.
If "object_list": self.queryset is called, the page will not be updated after refresh.
Yes, that is because the get_queryset() in Django's ListView does not directly return the queryset, it will return self.queryset.all(). Indeed, if we look at the source code [GitHub]:
def get_queryset(self):
# …
if self.queryset is not None:
queryset = self.queryset
if isinstance(queryset, QuerySet):
queryset = queryset.all() # ← makes a clone
elif self.model is not None:
queryset = self.model._default_manager.all()
else:
# …
# …
It thus returns a self.queryset.all() and this is a QuerySet that has not been loaded (yet), and thus will, when enumerating over it, make a new database query.
You thus can implement this with:
class CoursesListView(View):
template_name = 'courses/courses_list.html'
queryset = CoursersModels.objects.all()
def get(self,request, *args, **kwargs):
context = {
'object_list': self.queryset.all()
}
return render(request,self.template_name,context)
That being said, instead of starting with a "simple" View, you can make use of Django's ListView:
from django.views.generic import ListView
# work with a ListView ↓
class CoursesListView(ListView):
template_name = 'courses/courses_list.html'
model = CoursersModels

get context data in get_queryset

I have BaseContext and Listview which is for Searching in multiple models, Search Class inherits from BaseContext. I set the current user to context and want to use it in my def get_queryset method, But it doesn't work. I think in Search CBV get_context_data execute after get_queryset that's why, self.user is None.
class BaseContext(ContextMixin):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
request = self.request
if request.COOKIES.get('token') is not None:
...
user = Users.objects.filter(user_id=user_id).first()
context.update({'current_user': user})
context.update({'is_logged_in ': True})
else:
context.update({'is_logged_in ': False})
return context
class Search(BaseContext, ListView):
template_name = 'search.html'
context_object_name = "results"
paginate_by = 15
user = None
def get_queryset(self):
query = self.request.GET.get('search', None)
if query is not None and self.user is not None:
...
return queryset_chain
return faqModel.objects.none()
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
if 'is_logged_in' in context and context['is_logged_in']:
self.user = context['current_user']
else:
redirect("index")
return context
My question is how can I get context data in def get_queryset(self)?
for Listview get_quersyset() is called before get_contex_data() , hence getting context data is not possible in get_queryset()

Django: passing a filtered Queryset to a ListView?

Using Django Class Based Views, I already have a ListView for Order objects, and I created a FormView to perform advanced searches on these orderers.
However, I'm not sure how to pass the filtered queryset of the FormView to the ListView.
Here is the code, with commented sections to explain the issue:
class OrdersListView(PermissionRequiredCanHandleOrders,
SelectRelatedMixin, PrefetchRelatedMixin,
ModelInContextMixin, SubSectionLastOrders,
RestaurantOrdersOnly,
ListView):
model = Order
paginator_class = DiggPaginator
paginate_by = 15
select_related = ('convive__user',)
prefetch_related = ('orderoperation_set',)
# will use the template named order_list.html
class OrdersAdvancedSearchView(PermissionRequiredCanHandleOrders,
ModelInContextMixin, SubSectionLastOrders,
RestaurantOrdersOnly, RestaurantMixin,
FormView):
model = Order
template_name = "orders/order_advanced_search.html"
form_class = OrderAdvancedSearchForm
def form_valid(self, form):
data = form.cleaned_data
queryset = Order.objects.all()
# Here, I'm using the form content to filter the queryset
# queryset = queryset.filter(some_attribute__in=data['stuff'])
# MY PAIN POINT IS HERE: what I'm supposed to do here ?
# my queryset is filtered, and I need to call OrderListView
# with it.
return super().form_valid(form)
You should use the ListView to filter the queryset with the GET request parameters. For this, add the form to your context and process it when getting the queryset:
def get_queryset(self):
self.form = OrderAdvancedSearchForm(data=self.request.GET or None)
if self.request.GET and form.is_valid():
# filter using the form's cleaned_data
queryset = super().get_queryset().filter(...)
else:
queryset = super().get_queryset()
return queryset
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) # this calls self.get_queryset() which assigns self.form
context['form'] = self.form
return context
Now in your template you can just render the same form, except its method should be "GET" not "POST".

Django: Modify Class Based View Context (with **kwargs)

I've a Function definition that works perfect, but I need to update to a Class Based View.
function def:
def ProdCatDetail(request, c_slug, product_slug):
try:
product = Product.objects.get(category__slug=c_slug, slug = product_slug)
except Exception as e:
raise e
return render(request, 'shop/product.html', {'product':product})
So far, I've read that to modify the context of a Class Based View (CBV) I need to overwrite the def get_context_data(self, **kwargs) in the CBV.
So, I've done this:
Class Based View:
class ProdCatDetailView(FormView):
form_class = ProdCatDetailForm
template_name = 'shop/product.html'
success_url = 'shop/subir-arte'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['product'] = Product.objects.get(category__slug=c_slug, slug = product_slug)
return context
How should I pass the arguments c_slug, product_slug to the get_context_data definition for this CBV to work as the Function definition?
A class based view is, by the .as_view basically used as a function-based view. The positional and named parameters, are stored in self.args, and self.kwargs respectively, so we can use:
class ProdCatDetailView(FormView):
# ...
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['product'] = Product.objects.get(
category__slug=self.kwargs['c_slug'],
slug =self.kwargs['product_slug']
)
return context

Modify context after using form

I have a view that shows a list of objects that have specific tag.
class AllView(ListView):
context_object_name = 'facts'
template_name = 'facts_blog/all_facts.html'
def get_context_data(self, **kwargs):
context = super(AllView, self).get_context_data(**kwargs)
if 'TagForm' not in context:
context['TagForm'] = TagForm()
return context
def get_queryset(self):
form = TagForm(self.request.GET)
if form.is_valid():
context = RequestContext(self.request)
return self.send_results(form.cleaned_data['tag'])
else:
return Fact.objects.all()
def send_results(self, tag):
return Fact.objects.filter(tags__slug=tag)
I want to return form.cleaned_data['tag'] to template, but i have already used get_context_data... What should I do to do this?
If I'm understanding what you're saying, you want to include the "tag" in your context dict while the ListView queryset will return the "Fact" instances associated with the tag. Correct?
If you look at Django's BaseListView the get method will call get_queryset first, then right before it calls render_to_response it will call your get_context_data method:
class BaseListView(MultipleObjectMixin, View):
"""
A base view for displaying a list of objects.
"""
def get(self, request, *args, **kwargs):
self.object_list = self.get_queryset()
allow_empty = self.get_allow_empty()
# [snip]
# some code not related to this omitted
# [snip]
context = self.get_context_data()
return self.render_to_response(context)
What you can do is set an attribute (self.object) in your get_queryset method to store the "tag" from your validated form, then retrieve it in your get_context_data method so you can stuff it in the context dict:
def get_queryset(self):
form = TagForm(self.request.GET)
if form.is_valid():
context = RequestContext(self.request)
# set self.object to the "tag"
self.object = form.cleaned_data['tag']
return self.send_results(self.object)
else:
# set self.object to None since the form is not validated
self.object = None
return Fact.objects.all()
def get_context_data(self, **kwargs):
context = super(AllView, self).get_context_data(**kwargs)
if 'TagForm' not in context:
context['TagForm'] = TagForm()
# put self.object in the context dict
context['tag'] = self.object
return context
Your template will then have a var called tag with either the "tag" value or None.
Assuming that the form's action returns to the AllView view via GET request, you can modify the get_context_data in this way:
def get_context_data(self, **kwargs):
context = super(AllView, self).get_context_data(**kwargs)
if 'TagForm' not in context:
context['TagForm'] = TagForm()
tag_form = TagForm(self.request.GET)
if tag_form.is_valid():
context['tag'] = tag_form.cleaned_data.get('tag')
return context
Hope it does the trick.

Categories

Resources