Context overrides context_object_name - python

I am new to Django and pretty lost on how to approach this problem.
Views.py:
class UserProfileDetailView(DetailView):
model = CustomUser
context_object_name = 'profile_detail'
template_name = 'user_profile_templates/profile_detail.html'
def get(self, request, pk, *args, **kwargs):
followers_var = request.user.motivators.all()
if len(followers_var) == 0:
is_following = False
for follower in followers_var:
if follower == request.user:
is_following = True
break
else:
is_following = False
number_of_followers = len(followers_var)
context = {
'number_of_followers': number_of_followers,
'is_following': is_following,
}
return render(request, 'user_profile_templates/profile_detail.html', context,)
So the problem is that I can't use context_object_name or 'object.var_name' in my template I guess that context that I defined in my get method is responsible for that. I've read somewhere that we should not use get() method in DetailView class, but I don't know what to do instead, should I use View and go from there? RequestContext? How would you refactor this whole class?

When you are using DetailView, you can put the code that deals with context in get_context_data:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
... # your code
context["number_of_followers"] = number_of_followers,
context["is_following"] = is_following,

Related

How to get userid in Django classviews?

I have this class that changes user password:
class PasswordsChangeView(PasswordChangeView):
from_class = PasswordChangingForm
success_url = reverse_lazy(
"base:password/passwordsuccess",
)
And I would like to insert User Avatar Img Path in context.
This Img is inside a UserComplement model.
When I create function views, it works doing:
#login_required
def password_success(request):
try:
obg = UserComplement.objects.get(pk=request.user.id)
context = {
"img_avatar": obg.avatar_image,
}
return render(request, "registration/passwordsuccess.html", context)
except UserComplement.DoesNotExist:
return render(request, "registration/passwordsuccess.html")
The problem is that I cannot get the userID in classviews to pass it through the context on success_url.
It was a lack of knowledge on how class based views work. The function get_context_data did the job well, returning the context.
class PasswordsChangeView(PasswordChangeView):
from_class = PasswordChangingForm
success_url = reverse_lazy(
"base:password/passwordsuccess",
)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
obj = UserComplement.objects.get(pk=self.request.user.id)
context["img_avatar"] = obj.avatar_image
return context

django render and validate formset in class based-view (ListView)

I have the following class based view that I want to use to render a formset and validate it when it gets submitted through a post method:
The formset renders perfectly. When I submit the form I can read the formset and check it for errors. in the post method of this class -> errors = backorder_formset.errors
If I find any errors in the formset, I would like to render the view, but this time with the formset instance, that I read from POST.
When I call ctx = self.get_context_data() form within the post method of the class the following error gets raised from the call super(MissingProductsListView, self).get_context_data(*args, **kwargs):
'MissingProductsListView' object has no attribute 'object_list'
It seems like the superclass of Listview performs this call:queryset = kwargs.pop('object_list', self.object_list)
My question is why am I running in this error? and how could I render this formset with its errors messages to display it in the template after it was posted? I am using Django 1.9.9
class MissingProductsListView(generic.ListView):
template_name = 'dashboard/purchaseorder/missing_products.html'
context_object_name = 'backorders'
model = BackOrder
def post(self, request, *args, **kwargs):
backorder_formset = BackOrderFormset(request.POST)
errors = backorder_formset.errors
if backorder_formset.is_valid():
# <process form cleaned data>
return HttpResponseRedirect('/success/')
else:
ctx = self.get_context_data()
return self.render_to_response(ctx)
def accumulate_identical_products_from_backorders(self, back_order_list):
... some code
return sorted_accumulated_dict.values()
def get_context_data(self, *args, **kwargs):
ctx = super(MissingProductsListView, self).get_context_data(*args, **kwargs)
ctx['title'] = _("Missing Products")
if self.request.POST:
ctx['back_order_formset'] = BackOrderFormset(self.request.POST)
else:
accumulated_backorders_per_product = self.accumulate_identical_products_from_backorders(BackOrder.objects.all())
back_orders = BackOrderFormset(initial=[{'product_id': backorder_dict['product_id'],
'product': backorder_dict['title'],
'quantity': backorder_dict['quantity']} for backorder_dict in
accumulated_backorders_per_product])
ctx['back_order_formset'] = back_orders
return ctx
def get_queryset(self):
.. some code
return backorder_list
Look here:
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()
if not allow_empty:
# When pagination is enabled and object_list is a queryset,
# it's better to do a cheap query than to load the unpaginated
# queryset in memory.
if self.get_paginate_by(self.object_list) is not None and hasattr(self.object_list, 'exists'):
is_empty = not self.object_list.exists()
else:
is_empty = len(self.object_list) == 0
if is_empty:
raise Http404(_("Empty list and '%(class_name)s.allow_empty' is False.") % {
'class_name': self.__class__.__name__,
})
context = self.get_context_data()
return self.render_to_response(context)
Basically - you missed this part in the POST handler:
self.object_list = self.get_queryset()
And to be honest - I am not quite sure if this is a good idea to add post to the generic ListView in django. It looks more like FormView - but I can be wrong here.

Pass context data from generic.DetailView

How can i pass the context data which is coming from a forms.py to my views class which is using generic detailView, i need to pass forms.py to my product detail page.
Here is the code for my view class
class ProductView(generic.DetailView):
model = Product
cart_product_form = CartAddProductForm()
context = {'cart_product_form': cart_product_form}
template_name = 'shopping/product.html'
query_pk_and_slug = True
Please let me know if this is incorrect
Override get_context_data, and add the form to the context before returning it.
class ProductView(generic.DetailView):
model = Product
template_name = 'shopping/product.html'
query_pk_and_slug = True
def get_context_data(self, **kwargs):
context = super(ProductView, self).get_context_data(**kwargs)
cart_product_form = CartAddProductForm()
context['cart_product_form'] = cart_product_form
return context

Django 1.9 FormView never reach get_context_data

I have a FormView called LeagueTransferView based on a form LeagueTransferForm.
I'm trying to override get_context_data to add extra players to render in the template.
But get_context_data is never reached. It's working fine on other views like, DetailView, ListView,...
I'm missing something?
Below my configuration
View
class LeagueTransferView(FormView):
template_name = 'hockey/league/transfer_market.html'
form_class = LeagueTransferForm
success_url = ''
def get_context_data(self, **kwargs):
print('----NEVER REACHED----')
context = super(LeagueTransferView, self).get_context_data(**kwargs)
petitioner = get_object_or_404(Team, user=self.request.user.profile, league=self.kwargs['pk'])
context['players'] = Player.objects.filter(leagues=self.kwargs['pk']).exclude(teams=petitioner)
return context
def get(self, request, *args, **kwargs):
petitioner = get_object_or_404(Team, user=self.request.user.profile, league=self.kwargs['pk'])
form = self.form_class(initial={'league': self.kwargs['pk'], 'petitioner': petitioner})
form.fields['offered_player'].queryset = petitioner.players
return render(request, self.template_name, {'form': form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
transfer = form.save(commit=False)
team = Team.objects.filter(league=transfer.league, players__in=[transfer.requested_player])
if not team: # free agent
transfer.status = 1
messages.success(request, _('transfer succeeded'))
else:
print(team)
transfer.player_owner = team[0]
if transfer.petitioner.user is None: # bot team
transfer.status = 1
messages.success(request, _('transfer succeeded'))
else:
messages.success(request, _('transfer waiting for confirmation by player owner'))
transfer.save()
return HttpResponseRedirect(reverse('hockey_dashboard'))
petitioner = get_object_or_404(Team, user=self.request.user.profile, league=self.kwargs['pk'])
form.fields['offered_player'].queryset = petitioner.players
return render(request, self.template_name, {'form': form})
FORM
class LeagueTransferForm(forms.ModelForm):
class Meta:
model = Transfer
fields = ['league', 'requested_player', 'offered_player', 'player_owner', 'petitioner']
labels = {
'requested_player': _('Requested player'),
'offered_player': _('Offered player'),
}
widgets = {
'requested_player': forms.HiddenInput,
'league': forms.HiddenInput,
'player_owner': forms.HiddenInput,
'petitioner': forms.HiddenInput
}
Your code is never reaching get_context_data() because you have overridden the get() method and not calling the get_context_data() function there. You need to manually call the get_context_data() function at the time of passing context to render() in your code.
Instead of doing that, i would suggest you to try the below approach where instead of overrriding get() and returning your custom response, you only override what is necessary and let Django handle the rest.
class LeagueTransferView(FormView):
template_name = 'hockey/league/transfer_market.html'
form_class = LeagueTransferForm
success_url = ''
def get_context_data(self, **kwargs):
context = super(LeagueTransferView, self).get_context_data(**kwargs)
context['players'] = Player.objects.filter(leagues=self.kwargs['pk']).exclude(teams=self.petitioner)
return context
def get_initial(self):
initial = super(LeagueTransferView, self).get_initial()
initial['league'] = self.kwargs['pk'] # add custom data to initial
initial['petitioner'] = self.petitioner # add custom data to initial
return initial
def get_form(self, form_class=None):
form = super(LeagueTransferView, self).get_form(form_class)
# override the queryset
form.fields['offered_player'].queryset = self.petitioner.players
return form
def get(self, request, *args, **kwargs):
# only perform 1 query to get 'petitioner'
self.petitioner = get_object_or_404(Team, user=self.request.user.profile, league=self.kwargs['pk'])
return super(LeagueTransferView, self).get(request, *args, **kwargs)

How can I return regular response in override get method, Django

I try to use class based views in Django. And I have such problem: I define a base class for a blog (BlogBaseView) and two other classes, that inherit it.
And in the second class(BlogIndexView) I want to make the search by get request, so I have override get method. It works, but if I don't make get request, it returns HttpResponse, however I want to return usual context (which BlogIndexView retunes without override get method).
What can I do?
class BlogBaseView(View):
def get_context_data(self, **kwargs):
context = super(BlogBaseView, self).get_context_data(**kwargs)
blog_categories = []
categories = BlogCategory.objects.all()
for category in categories:
blog_categories.append(tuple([category, category.get_number_of_category_items]))
context['name_page'] = 'blog'
context['tags'] = Tag.objects.all()
context['blog_categories'] = blog_categories
return context
class BlogIndexView(BlogBaseView, ListView):
queryset = Post.objects.all().order_by('-date_create')
template_name = 'index_blog.html'
context_object_name = 'posts'
def get(self, request):
if request.GET.get('tag'):
context = {
'posts' : Post.objects.filter(tags__name__in=[request.GET.get('tag')])
}
return render(request, self.template_name, context)
return HttpResponse('result')
class BlogFullPostView(BlogBaseView, DetailView):
model = Post
template_name = 'full_post.html'
pk_url_kwarg = 'post_id'
context_object_name = 'post'
Thanks!
ListView class also has a get_context_data method, so you should override that instead of get method. Using super you'll get access to BlogBaseView.get_context_data and then you can extended the result.
Here's how:
class BlogIndexView(BlogBaseView, ListView):
queryset = Post.objects.all().order_by('-date_create')
template_name = 'index_blog.html'
context_object_name = 'posts'
def get_context_data(self, **kwargs):
# use the (super) force Luke
context = super(BlogIndexView, self).get_context_data(**kwargs)
if self.request.GET.get('tag'):
context['posts'] = Post.objects.filter(tags__name__in=[self.request.GET.get('tag')])
return context
If you are overriding ListView then it's not a good idea to override the get method, as you will lose a lot of the ListView functionality.
In this case, it would be a better idea to override get_queryset, and do the search there.
def get_queryset(self):
queryset = super(BlogIndexView, self). get_queryset()
if request.GET.get('tag'):
queryset = queryset.filter(tags__name=request.GET['tag'])
return queryset

Categories