get context data in get_queryset - python

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()

Related

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

Django class-based-view access context data from get_queryset

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)

Django: How to pass in context variables for registration redux?

here is the views.py for registration redux. i am a little bit confused and i don't really understand how to pass in context variables.
class RegistrationView(BaseRegistrationView):
SEND_ACTIVATION_EMAIL = getattr(settings, 'SEND_ACTIVATION_EMAIL', True)
success_url = 'registration_complete'
def register(self, request, form):
if Site._meta.installed:
site = Site.objects.get_current()
else:
site = RequestSite(request)
if hasattr(form, 'save'):
new_user_instance = form.save()
else:
new_user_instance = UserModel().objects.create_user(**form.cleaned_data)
new_user = RegistrationProfile.objects.create_inactive_user(
new_user=new_user_instance,
site=site,
send_email=self.SEND_ACTIVATION_EMAIL,
request=request,
)
signals.user_registered.send(sender=self.__class__,
user=new_user,
request=request)
return new_user
BaseRegistrationView inherits from FormView, which has in its inheritance chain django.views.generic.base.ContextMixin. This defines the get_context_data method, which returns context as a dict. You can override that method and add in your own variables like so:
class RegistrationView(BaseRegistrationView):
...
def get_context_data(self, **kwargs):
context = super(RegistrationView, self).get_context_data(**kwargs)
context['key'] = value
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.

django update view and passing context

I have a update view:
class GeneralUserUpdateView(UpdateView):
model = GeneralUser
form_class = GeneralUserChangeForm
template_name = "general_user_change.html"
def dispatch(self, *args, **kwargs):
return super(GeneralUserUpdateView, self).dispatch(*args, **kwargs)
def post(self, request, pk, username):
self.pk = pk
self.username = username
self.gnu = GeneralUser.objects.get(pk=self.pk)
#form = self.form_class(request.POST, request.FILES)
return super(GeneralUserUpdateView, self).post(request, pk)
def form_valid(self, form, *args, **kwargs):
self.gnu.username = form.cleaned_data['username']
self.gnu.email = form.cleaned_data['email']
self.gnu.first_name = form.cleaned_data['first_name']
self.gnu.last_name = form.cleaned_data['last_name']
self.gnu.address = form.cleaned_data['address']
self.gnu.save()
return redirect("user_profile", self.pk, self.username)
Here in this view I want to pass a context like:
context['picture'] = GeneralUser.objects.get(pk=self.pk)
I did trying get_context_data but I cant access pk in there..
Am I doing the update right?? How can I pass that context in there??
You shouldn't be overriding post at all. All of that logic should happen in get_context_data.
In fact, none of your overrides are needed. Everything that you do in form_valid will be done already by the standard form save. And overriding dispatch just to call the superclass is pointless.
Your view should look like this only, with no overridden methods at all:
class GeneralUserUpdateView(UpdateView):
model = GeneralUser
form_class = GeneralUserChangeForm
template_name = "general_user_change.html"
context_object_name = 'picture'
(although it seems a little odd that you want to refer to an instance of GeneralUser as "picture").
Edit to redirect to a specific URL, you can define get_success_url:
def get_success_url(self):
return reverse("user_profile", self.kwargs['pk'], self.kwargs['username'])

Categories

Resources