my code is
class Leads(LoginRequiredMixin, ListView):
def get_queryset(self):
q = self.request.GET.get('q', "all")
if q == "customer":
qs = alllead.objects.filter(isCustomer="yes")
elif q == "lead":
qs = alllead.objects.filter(isCustomer="no")
else:
qs = self.queryset
return qs
def get_context_data(self, **kwargs):
context = super(Leads, self).get_context_data(**kwargs)
count = self.queryset.count()
context['count'] = count or "000"
return context
so i am filtering my queryset and also i need to set record count as context parameter "count"
but the number of record is not updating
so i have changed my code to
def get_queryset(self):
q = self.request.GET.get('q', "all")
if q == "customer":
qs = alllead.objects.filter(isCustomer="yes")
elif q == "lead":
qs = alllead.objects.filter(isCustomer="no")
else:
qs = self.queryset
return qs
def get_context_data(self, **kwargs):
context = super(Leads, self).get_context_data(**kwargs)
q = self.request.GET.get('q', "none")
if q == "customer":
count = alllead.objects.filter(isCustomer="yes").count()
elif q == "lead":
count = alllead.objects.filter(isCustomer="no").count()
else:
count = self.queryset.count()
context['count'] = count or "000"
return context
i think i am duplicating code and this is not the proper way to do it.
can anyone suggest me the optimal way to update
context['count'] = qs.count()
#inside get_queryset()
Once you've called the superclass get_queryset(), then the qs is added to the context as alllead_list; you can just access it from there.
def get_context_data(self, **kwargs):
context = super(Leads, self).get_context_data(**kwargs)
count = context['alllead_list'].count()
However, I don't think you need to do this in the view at all; you can just as easily do this in the template by accessing {{ allead_list.count|default:"000" }}.
Edit Since the queryset is paginated, you can get the count directly from the paginator: context['paginator'].count or in the template {{ paginator.count }}.
Related
So I'm building an e-commerce store with Django(First project after learning). I need to click on Sort in the template, and have the CBV return an object that's ordered by either, price, or whatever field I specify in the request. This is what I have so far
Template
Sort by Lowest Price
View
class ClotheListView(ListView):
model = Clothe
paginate_by = 8
def get_filter_param(self):
# Grab the absolute url and then retrieve the filter param
filter_param = self.request.path.split("/")[-1]
return filter_param
def get_queryset(self):
filter_param = self.get_filter_param()
if(filter_param != ""):
queryset = self.model.objects.filter(cloth_gender=filter_param)
else:
queryset = self.model.objects.all()
return queryset
return clothes_filtered_list.qs
def get_ordering(self):
ordering = self.request.GET.get('ordering', '-cloth_price')
return ordering
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
return context
Url.py
urlpatterns = [
path('', views.ClotheListView.as_view(), name="clothe_list"),
path('<slug:slug>', views.ClotheListView.as_view(),
name="clothe_list_category"),
path('<int:pk>/', views.ClotheDetailView.as_view(), name="clothe_detail")
]
ok, so this is how I did it:
Template
<span class="float-end d-none d-lg-block mt-3">
Sort by: Price -
High To Low |
Price:
Low to High
</span>
View
class ClotheListView(ListView):
model = Clothe
paginate_by = 8
def get_filter_param(self):
# Grab the absolute url and then retrieve the filter param
filter_param = self.request.path.split("/")[-1]
return filter_param
def get_queryset(self):
# code for price sorting
default_order = "cloth_name"
order_param = ""
user_filter = ""
try:
order_param = self.request.GET.get('ordering').strip()
except:
pass
try:
user_filter = self.request.GET.get('filter').strip()
except:
pass
order_by = order_param or default_order
# End of sorting code
filter_param = self.get_filter_param()
if(filter_param != "" or not filter_param):
if(user_filter != ""):
queryset = self.model.objects.filter(
cloth_gender=filter_param, cloth_category=user_filter)
else:
queryset = self.model.objects.filter(
cloth_gender=filter_param)
else:
queryset = self.model.objects.all()
return queryset.order_by(order_by)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
return context
url.py
urlpatterns = [
path('', views.ClotheListView.as_view(), name="clothe_list"),
path('<slug:slug>', views.ClotheListView.as_view(),
name="clothe_list_category"),
path('<int:pk>/', views.ClotheDetailView.as_view(), name="clothe_detail")
]
I use a CB ListView for displaying objects. I want to add a session variable based on another models' PK during the execution of my ListView:
views.py
class ProduitListView(LoginRequiredMixin, ListView):
model = Produit
context_object_name = "produits"
paginate_by = 10
template_name = 'products/produits.html'
ordering = ['-mageid', ]
def get_context_data(self, *args, **kwargs):
context = super(ProduitListView, self).get_context_data(
*args, **kwargs)
# used for incoming products (sourcing cf URLS)
supplier_pk = self.kwargs.get('pk', None)
if supplier_pk:
set_incoming_supplier(self.request, supplier_pk)
context['avail_warehouses'] = Warehouse.objects.all()
context['js_warehouses'] = serialize(
'json', Warehouse.objects.all(), fields=('code', 'id', ))
context['title'] = 'Produits'
return context
set_incoming_supplier (in another APP)
#login_required
def set_incoming_supplier(request, pk):
supplier = Supplier.objects.filter(pk=pk).first()
supp = SupplierSerializer(instance=supplier).data
rs = request.session
if 'income' in rs:
if 'cur_supplier' in rs['income']:
prev_supplier = rs['income']['cur_supplier']
if supp != prev_supplier:
return render(request, 'sourcing/alert_supplier_change.html',
{'prev_supplier': prev_supplier, 'cur_supplier': rs['income']['cur_supplier']})
rs['income'] = {'cur_supplier': supp}
I thought the return render(request, 'sourcing/alert_supplier_change... could "break" my ListView and render my alert page but it doesn't. ListView seems to continue and finally renders my ProduitListView page.
Why doesn't this work ?
Finally found a solution that consists in using get() method within my CBV. In it, I evaluate my supplier with set_incoming_supplier() that returns a context or None. According to this evaluation, I render either the regular template or my alert template.
ProduitListView(LoginRequiredMixin, ListView):
class ProduitListView(LoginRequiredMixin, ListView):
model = Produit
context_object_name = "produits"
paginate_by = 10
template_name = 'products/produits.html'
ordering = ['-mageid', ]
def get_context_data(self, *args, **kwargs):
context = super(ProduitListView, self).get_context_data(
*args, **kwargs)
context['avail_warehouses'] = Warehouse.objects.all()
context['js_warehouses'] = serialize(
'json', Warehouse.objects.all(), fields=('code', 'id', ))
context['title'] = 'Produits'
return context
def get(self, request, *args, **kwargs):
supplier_pk = self.kwargs.get('pk', None)
if supplier_pk:
context = set_incoming_supplier(self.request, supplier_pk)
if context:
return render(request, 'sourcing/alert_supplier_change.html', context)
return super().get(request, *args, **kwargs)
set_incoming_supplier()
def set_incoming_supplier(request, pk):
supplier = Supplier.objects.filter(pk=pk).first()
supp = SupplierSerializer(instance=supplier).data
rs = request.session
if 'income' in rs:
if 'cur_supplier' in rs['income']:
prev_supplier = rs['income']['cur_supplier']
if supp != prev_supplier:
return {'prev_supplier': prev_supplier, 'cur_supplier': supp}
rs['income'] = {'cur_supplier': supp}
Maybe not the best way but it works well.
I have implemented two views to display data according to the choice_fields but i have two views with slightly different logic in views and templates how do i combine them into one so that i take care of DRY
views.py:
class View1(LoginRequiredMixin,TemplateView):
template_name = 'temp1.html'
def get_context_data(self, **kwargs):
context = super(View1,self).get_context_data(**kwargs)
context['progress'] = self.kwargs.get('progress', 'in_progress')
if context['progress'] == 'in_progress':
context['objects'] = Model.objects.filter(progress='in_progress')
else:
context['objects'] = Model.objects.filter(progress__iexact=context['progress'], accepted=self.request.user)
return context
class View2(LoginRequiredMixin,TemplateView):
template_name = 'temp2.html'
def get_context_data(self, **kwargs):
context = super(View2,self).get_context_data(**kwargs)
context['progress'] = self.kwargs.get('progress', 'in_progress')
if context['progress'] == 'in_progress':
context['objects'] = Model.objects.filter(progress='in_progress',created = self.request.user)
else:
context['objects'] = Model.objects.filter(progress__iexact=context['progress'], created_by=self.request.user)
return context
Implement something like get_queryset in Class-based views.
class BaseView(LoginRequiredMixin,TemplateView):
""" requires subclassing to define template_name and
update_qs( qs, progress) method """
def get_context_data(self, **kwargs):
context = super(View1,self).get_context_data(**kwargs)
progress = self.kwargs.get('progress', 'in_progress')
if progress == 'in_progress':
qs = Model.objects.filter(progress='in_progress')
else:
qs = Model.objects.filter(progress__iexact=context['progress'] )
qs = self.update_qs( qs, (progress == 'in_progress') )
context['progress'] = progress
context['objects'] = qs
return context
class View1( BaseView):
template_name = 'temp1.html'
def update_qs( self, qs, in_progress):
if in_progress:
return qs
else:
return qs.filter( accepted=self.request.user)
class View2( BaseView):
template_name = 'temp2.html'
def update_qs( self, qs, in_progress):
if in_progress:
return qs.filter( created = self.request.user)
else:
return qs.filter( created_by=self.request.user)
I'm having to do some validation across both a form and formset. The £££ amount in the form must equal the sum of the amounts in the formset.
After a lot of Googling I found a solution where I add a custom init to the baseformset as follows:
class BaseSplitPaymentLineItemFormSet(BaseFormSet):
def __init__(self, cr=None, *args, **kwargs):
self._cr = cr
super().__init__(*args, **kwargs)
def clean(self):
if any(self.errors):
return
sum_dr = 0
for form in self.forms:
sum_dr += form.cleaned_data.get('dr')
if sum_dr != float(self._cr):
raise forms.ValidationError('The amount entered needs to equal the sum of the split payments.')
I then pass the amount value from the form when the formset is instantiated, so that the value can be used in the formset validation:
lineitem_formset = LineItemFormSet(form.data['amount'], request.POST)
This worked great for the create_new view which uses formset_factory(). This morning I wrote the update view using inline_formsetfactory(), but I now get an error:
__init__() got an unexpected keyword argument 'instance'
I only have a basic understanding of how the custom init works, so I can't find a solution to this error.
Forms.py:
class SplitPaymentForm(forms.Form):
date = forms.DateField(widget=DateTypeInput())
account = GroupedModelChoiceField(queryset=Ledger.objects.filter(coa_sub_group__type='a').order_by('coa_sub_group__name','name'), choices_groupby = 'coa_sub_group')
store = forms.CharField(required=True)
amount = forms.DecimalField(decimal_places=2)
class SplitPaymentLineItemForm(ModelForm):
ledger = GroupedModelChoiceField(queryset=Ledger.objects.all().order_by('coa_sub_group__name', 'name'), choices_groupby = 'coa_sub_group', empty_label="Ledger", required=True)
project = forms.ModelChoiceField(queryset=Project.objects.filter(status=0), empty_label="Project", required=False)
class Meta:
model = LineItem
fields = ['description','project', 'ledger','dr',]
# This init disallows empty formsets
def __init__(self, *arg, **kwarg):
super(SplitPaymentLineItemForm, self).__init__(*arg, **kwarg)
self.empty_permitted = False
class BaseSplitPaymentLineItemFormSet(BaseFormSet):
def __init__(self, cr=None, *args, **kwargs):
self._cr = cr
super().__init__(*args, **kwargs)
def clean(self):
if any(self.errors):
return
sum_dr = 0
for form in self.forms:
sum_dr += form.cleaned_data.get('dr')
if sum_dr != float(self._cr):
raise forms.ValidationError('The amount entered needs to equal the sum of the split payments.')
Views.py:
def split_payments_new(request):
LineItemFormSet = formset_factory(SplitPaymentLineItemForm, formset=BaseSplitPaymentLineItemFormSet, extra=2)
if request.method == 'POST':
form = SplitPaymentForm(request.POST)
lineitem_formset = LineItemFormSet(form.data['amount'], request.POST)
if form.is_valid() and lineitem_formset.is_valid():
q0 = JournalEntry(user=request.user, date=form.cleaned_data['date'], type="SP",)
q1 = LineItem(journal_entry=q0, description=form.cleaned_data['store'], ledger=form.cleaned_data['account'], cr=form.cleaned_data['amount'])
q0.save()
q1.save()
for lineitem in lineitem_formset:
q2 = LineItem(journal_entry=q0,description=lineitem.cleaned_data.get('description'),ledger=lineitem.cleaned_data.get('ledger'),project=lineitem.cleaned_data.get('project'),dr=lineitem.cleaned_data.get('dr'))
q2.save()
messages.success(request, "Split payment successfully created.")
return HttpResponseRedirect(reverse('journal:split_payments_show_detail', kwargs={'pk': q0.id}) )
else:
form = SplitPaymentForm(initial = {'date': datetime.date.today().strftime('%Y-%m-%d')})
lineitem_formset = LineItemFormSet()
return render(request, 'journal/split_payments_new.html', {'form': form, 'formset': lineitem_formset})
def split_payments_update(request, pk):
journal_entry = get_object_or_404(JournalEntry, pk=pk, type="SP")
lineitem = LineItem.objects.get(journal_entry=journal_entry.id, dr__isnull=True)
initial = {
'date': journal_entry.date.strftime('%Y-%m-%d'),
'account': lineitem.ledger,
'store': lineitem.description,
'amount': lineitem.cr,
}
form = SplitPaymentForm(initial=initial)
LineItemFormSet = inlineformset_factory(JournalEntry, LineItem, form=SplitPaymentLineItemForm, formset=BaseSplitPaymentLineItemFormSet, extra=0)
lineitem_formset = LineItemFormSet(instance=journal_entry)
if request.method == 'POST':
lineitem_formset = LineItemFormSet(form.data['amount'], request.POST, instance=journal_entry)
form = SplitPaymentForm(request.POST)
if lineitem_formset.is_valid() and form.is_valid():
lineitem_formset.save()
journal_entry.date = form.cleaned_data['date']
lineitem.ledger = form.cleaned_data['account']
lineitem.description = form.cleaned_data['store']
lineitem.cr = form.cleaned_data['amount']
journal_entry.save()
lineitem.save()
messages.success(request, "Split payment successfully updated.")
return HttpResponseRedirect(reverse('journal:split_payments_show_detail', kwargs={'pk': journal_entry.id}) )
return render(request, 'journal/split_payments_update.html',{'form': form, 'formset': lineitem_formset, 'journal_entry': journal_entry})
Solved. Just had to use BaseInlineFormSet.
I want to pass variable appuser to template and I don't understand how to do it.
I have tried to use kwargs.update but it still doesn't work.
I have a view:
class CausesView(AjaxFormView):
appuser = None
causes = []
cause_allocation_set = None
def prepare_request(self, request, *args, **kwargs):
self.causes = Cause.objects.filter(is_main_cause = True)
self.appuser = AppUser.get_login_user(request)
self.cause_allocation_set = set([r.cause_id for r in self.appuser.current_cause_save_point.cause_allocations_list])
def prepare_context(self, request, context, initial):
initial.update(
causes = self.cause_allocation_set,
appuser = self.appuser,
)
def prepare_form(self, request, form):
form._set_choices("causes", [(r.id, r.title) for r in self.causes])
def custom_context_data(self, request, **kwargs):
kwargs.update(
special_test = "dsf"
)
return kwargs
def process_form(self, request, form):
data = form.cleaned_data
try:
with transaction.atomic():
if self.cause_allocation_set != set(data.get('causes')):
self.appuser.save_causes(data.get('causes'))
except Exception as e:
message = "There was an error with saving the data: " + str(e.args)
return AjaxErrorResponse({'title':"Error", 'message':message})
return AjaxSuccessResponse('Causes Saved')
And I have a form:
class CauseForm(AjaxForm):
causes = forms.TypedMultipleChoiceField(label="Select Causes", choices = (), required = False, coerce = int,
widget = forms.CheckboxSelectMultiple())
def clean(self):
cleaned_data = super(CauseForm, self).clean()
causes = cleaned_data.get('causes')
validation_errors = []
if not causes is None and not len(causes):
validation_errors.append(forms.ValidationError("At least one Cause is required"))
if len(validation_errors):
raise forms.ValidationError(validation_errors)
return cleaned_data
How can I get variable appuser in temlpate?
For example:
{{ appuser.name }}
doesn't work.
Read How to use get_context_data in django
and
https://docs.djangoproject.com/en/1.9/ref/class-based-views/mixins-single-object/#django.views.generic.detail.SingleObjectMixin.get_context_data
Here is example of how you can do this
class CausesView(AjaxFormView):
...
def get_context_data(self, **kwargs):
context_data = super(CausesView, self).get_context_data(**kwargs)
context_data['appuser'] = self.appuser
return context_data