applying filter to other pages - python

i've got sort on main page of shop, it works , but in other pages it doesn't works. How could i apply this sort to other pages(sorted by category etc.)? at the url address it updates but no result, it just shows items in default order.i tried to find any answer but couldn't
view
class Shop(ListView):
template_name = 'essense/shop.html'
context_object_name = 'items'
paginate_by = 9
allow_empty = True
model = Item
def get_context_data(self, *, object_list=None, **kwargs):
context = super().get_context_data(**kwargs)
categories = Category.objects.all()
collections = Collection.objects.all()
brands = Brand.objects.all()
context['brands'] = brands
context['categories'] = categories
context['collections'] = collections
return context
def get_ordering(self):
return self.request.GET.get('orderby', )
class ShopCategory(Shop, ListView):
def get_queryset(self):
return Item.objects.filter(category__slug=self.kwargs['slug'])
def get_ordering(self):
return self.request.GET.get('orderby', )
template
<div class="product-sorting d-flex">
<p>Sort by:</p>
<form name="selectForm" action="{{request.get_full_path}}" method="get">
<label for="orderby"></label>
<select name="orderby" id="orderby" onchange="selectForm.submit();">
<option value="">default</option>
<option value="price">price: $ - $$</option>
<option value="-price">price: $$ - $</option>
</select>
<input type="submit" class="d-none" value="submit">
</form>
</div>
urls
urlpatterns = [
path('shop/', Shop.as_view(), name='shop'),
path('shop/subcategory/<slug:slug>', ShopBySubCategory.as_view(), name='shop_subcategory'),
path('shop/category/<slug:slug>/', ShopCategory.as_view(), name='shop_category'),
path('shop/on-sale', ItemsOnSale.as_view(), name='on_sale'),
path('shop/brand/<slug:slug>', ShopByBrand.as_view(), name='shop_brand'),
path('shop/<slug:slug>/', ItemDetail.as_view(), name='single_product')
]
move to categories
<div class="widget catagory mb-50">
<!-- Widget Title -->
<h6 class="widget-title mb-30">Categories</h6>
{% for cat in categories %}
<div class="catagories-menu">
<ul id="menu-content2" class="menu-content collapse show">
<!-- Single Item -->
<li data-toggle="collapse" data-target="#{{ cat.name }}">
{{ cat.name }}
<ul class="sub-menu collapse show" id="{{ cat.name }}">
<li>All</li>
{% for subcat in cat.category_subcategory.all %}
<li>{{ subcat.name }}</li>
{% endfor %}
</ul>
</li>
</ul>
</div>
{% endfor %}
template tag for filter
#register.simple_tag
def url_replace(request, field, value):
d = request.GET.copy()
d[field] = value
return d.urlencode()

Related

How to count the number of elements of each category in Django?

I have a list of elements. There is a 'status' value here. I need a code that in an HTML page will show the total number of items for each 'status'. How to do it?
For intance, there is the list in the page:
appnumber status
№54534534 accepted
%46342322 in process
%55745232 accepted
%67456452 denied
%76454534 accepted
%52864525 denied
%86752525 accepted
The result should be:
accepted - 4
denied - 2
in process - 1
My code:
home.html
<div class="headtext">
<form method="GET" action="{% url 'search' %}">
<input type="search" type="text" name="q" prequired placeholder="Put appnumber">
<button type="submit">Find</button>
</form>
</div>
<div>
{% for application in object_list %}
<div>
<p>Application: {{ application.appnumber }}, status: {{ application.status }}</p>
</div>
{% endfor %}
</div>
views.py
class HomeView(ListView):
model = Application
template_name = 'home.html'
class Search(ListView):
template_name = 'home.html'
def get_queryset(self):
return Application.objects.filter(appnumber__icontains=self.request.GET.get("q"))
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context["q"] = self.request.GET.get("q")
return context
You can use Count with annotate() so:
from django.db.models import Count
class HomeView(ListView):
model = Application
template_name = 'home.html'
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
status_counts = Application.objects.values('status').annotate(total=Count('status'))
context['status_counts'] = status_counts
return context
Then you can use status_counts in Template so:
<div class="headtext">
<form method="GET" action="{% url 'search' %}">
<input type="search" type="text" name="q" required placeholder="Put appnumber">
<button type="submit">Find</button>
</form>
</div>
<div>
{% for application in object_list %}
<div>
<p>Application: {{ application.appnumber }}, status: {{ application.status }}</p>
</div>
{% endfor %}
<h2>Status Counts</h2>
<ul>
{% for status_count in status_counts %}
<li>{{ status_count.status }} - {{ status_count.total }}</li>
{% endfor %}
</ul>
</div>

How to get all objects of a choice field?

I'm doing a search form where an user selects the category and the type of product which is a choice field of the Product model, product_type = models.CharField(max_length=30, choices=TYPE, default='Physical'). I can get the categories but not the choices in TYPE to add to the form.
choices.py
TYPE = [
('PHYSICAL', _('Physical')),
('DIGITAL', _('Digital')),
]
views.py
from .choices import TYPE
def is_valid_queryparam(param):
return param != '' and param is not None
def FilterView(request):
qs = Product.objects.all()
categories = Category.objects.all()
ptypes = Product.product_type.all() # The problem line
category = request.GET.get('category')
ptype = request.GET.get('ptype')
if is_valid_queryparam(category) and category != 'Choose...':
qs = qs.filter(category__name=category)
if is_valid_queryparam(ptype) and ptype != 'Choose...':
qs = qs.filter(product_type=ptype)
context = {
'queryset': qs,
'categories' : categories,
'ptypes' : ptypes,
}
return render(request, 'categories/display_query.html', context)
base.html
<div>
<h3>Advanced Search</h3>
<form method="GET" action="{% url 'filter-view' %}">
<div>
<label for="category">Category</label>
<select name="category">
<option selected>Choose...</option>
{% for cat in categories %}
<option value="{{ cat }}">{{ cat }}</option>
{% endfor %}
</select>
</div>
<div>
<label for="ptype">Product type</label>
<select name="ptype">
<option selected>Choose...</option>
{% for type in ptype %}
<option value="{{ type }}">{{ type }}</option>
{% endfor %}
</select>
</div>
<button type="submit">Search</button>
</form>
</div>
display_query.html
{% extends 'products/base.html' %}
{% block content %}
<div>
<ul>
{% for product in queryset %}
<li>
{{ product.title }}
<span>Author: {{ product.author.username }}</span>
</li>
{% endfor %}
</ul>
</div>
{% endblock content %}
You can use the given code:
p_types_keys = [i[0] for i in TYPE]
p_types_values = [i[1] for i in TYPE]
ptype_dict = dict(zip(p_types_keys, p_types_values))
In the line will return all the keys of your "TYPE"
The second line will return all the keys of your "TYPE"
The Third line will return the key-value pair of your types to add form.

Add product according to category in Django

I am new in Django. I want to add product against the category which I select, but I don't know how can I do that. I select the category and add product but nothing happened. I don't know how can I do this. Thank you in advance
Model.py
class Product(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
name = models.CharField(max_length=30)
price = models.IntegerField()
view.py
class SelectCategory(TemplateView):
template_name = 'purchase/selectCategory.html'
def get(self, request, *args, **kwargs):
categories = CategoryModel.objects.all()
args = {'categories': categories}
return render(request, self.template_name, args)
def post(self, request):
try:
data = self.request.POST.get
categoryId = ProductModel.category['category.id'].id
product = ProductModel(
category_id=data(categoryId),
name=data('name'),
price=data('price')
)
product.save()
return redirect('menu')
except Exception as e:
return HttpResponse('failed{}'.format(e))
Template
{% extends 'auth/home.html' %}
{% block content %}
<form method="get">
{% csrf_token %}
<label> Select Category
<select name="Select Category">
<option disabled="disabled" selected> Select Category</option>
{% for category in categories %}
<option value={{ category.id }}>
{{ category.name }}
</option>
{% endfor %}
</select>
</label>
<input type="submit" value="Select">
</form>
<form method="post">
{% csrf_token %}
<label for="name">Name
<input type="text" name="name" id="name" placeholder="Enter Product name">
</label>
<label for="price">Price
<input type="number" name="price" id="price" placeholder="Enter Price of Product">
</label>
<button type="submit">Submit</button>
<button>Menu</button>
</form>
{% endblock %}
Get the category instance first using data selected category id and then push that instance to as a parameter to productmodel.
def post(self, request):
try:
data = self.request.POST.get
categoryObj= CategoryModel.objects.get(I'd=data[])
product = ProductModel(
category_id= categoryObj
name=data('name'),
price=data('price')
)
product.save()
return redirect('menu')
except Exception as e:
return HttpResponse('failed{}'.format(e))

Django - template - reduce for loop repetition, reduce sqlite3 db query

I find myself repeatedly cycling through the same set of data, accessing database several time for the same loops to achieve displaying the correct data on one template, here's the code:
<!-- item images and thumbnails -->
<div class="row">
<div class="col-12 col-sm-8">
<div id="item{{item.pk}}Carousel" class="carousel slide" data-ride="carousel">
<ol class="carousel-indicators">
{% for image in item.itemimage_set.all %}
<li data-target="#item{{item.pk}}Carousel" data-slide-to="{{forloop.counter0}}"
{% if forloop.first %} class="active" {% endif %}></li>
{% endfor %}
</ol>
<div class="carousel-inner shadow-lg rounded-sm">
{% for image in item.itemimage_set.all %}
<div class="carousel-item {% if forloop.first %} active {% endif %}">
<img src="{{image.image.url}}" class="d-block w-100" alt="...">
</div>
{% endfor %}
</div>
{% if item.itemimage_set.count > 1 %}
<a class="carousel-control-prev" href="#item{{item.pk}}Carousel" role="button" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="carousel-control-next" href="#item{{item.pk}}Carousel" role="button" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
{% endif %}
</div>
</div>
<div class="pl-sm-0 col-12 col-sm-4 d-flex flex-wrap align-content-start">
{% for image in item.itemimage_set.all %}
<div class="col-4
{% if item.itemimage_set.count > 3 %}
col-sm-6
{% else %}
col-sm-8
{% endif %}
mt-2 px-1 mt-sm-0 pb-sm-2 pt-sm-0 mb-0">
<img src="{{image.image.url}}" alt="" class="col-12 p-0 rounded-sm shadow-sm"
data-target="#item{{item.pk}}Carousel" data-slide-to="{{forloop.counter0}}">
</div>
{% endfor %}
</div>
</div>
<!-- /item images and thumbnails -->
The above code renders the item's itemimage bootstrap carousel and on the same page, an extra carousel modal:
<!-- itemImageModal -->
<div class="modal fade" id="itemImageModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle"
aria-hidden="true">
<div class="modal-dialog modal-dialog-centered col-12 col-md-8 modal-lg" role="document">
<div class="modal-content">
<div class="col-12 px-0">
<div id="itemImage{{item.pk}}Carousel" class="carousel slide" data-ride="carousel">
<ol class="carousel-indicators">
{% for image in item.itemimage_set.all %}
<li data-target="#itemImage{{item.pk}}Carousel" data-slide-to="{{forloop.counter0}}"
{% if forloop.first %} class="active" {% endif %}></li>
{% endfor %}
</ol>
<div class="carousel-inner shadow-lg rounded-sm">
{% for image in item.itemimage_set.all %}
<div class="carousel-item {% if forloop.first %} active {% endif %}">
<img src="{{image.image.url}}" class="d-block w-100" alt="...">
</div>
{% endfor %}
</div>
{% if item.itemimage_set.count > 1 %}
<a class="carousel-control-prev" href="#itemImage{{item.pk}}Carousel" role="button" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="carousel-control-next" href="#itemImage{{item.pk}}Carousel" role="button" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
{% endif %}
</div>
</div>
</div>
</div>
</div>
Data structure:
class Item(models.Model):
name = ... etc.
class ItemImage(models.Model):
item = models.ForeignKey(Item, on_delete=models.CASCADE)
image = models.ImageField(upload_to='itemimages', null=True, blank=True)
As you can see, there are 5 forloop cycles in the template which leads to frequent queries from the database over the same set of data.
What I've tried:
I've tried to replace {% for image in item.itemimage_set.all %} with {% for image in item.load_related_itemimage %}, and in the models.py:
class Item(models.Model):
name = ...
def load_related_itemimage(self):
return self.itemimage_set.prefetch_related('image')
and this prompt error:
'image' does not resolve to an item that supports prefetching - this is an invalid parameter to prefetch_related().
I'm actually quite new to django and am not sure how to use select_related or prefetch_related but so far I've employed them and managed to reduce database query from 150 to 30+. And I think the frequency can be further reduced due to the silly cycles above as you can see.
forward the Debug Toolbar data for the page:
SELECT "appname_itemimage"."id",
"appname_itemimage"."item_id",
"appname_itemimage"."image"
FROM "appname_itemimage"
WHERE "appname_itemimage"."item_id" = '19'
5 similar queries. Duplicated 5 times.
5 similar queries. Duplicated 5 times. is bad right?
view.py
class ItemDetailView(DetailView):
'''display an individual item'''
model = Item
template_name = 'boutique/item.html'
Thanks for #Iain Shelvington's answer, his solution for the above code works like a charm, how about this:
I think they are related so I didn't separate this to raise another question -- what if the item itself is in a forloop? how to use prefetch_related in this situation? thanks!
{% for item in subcategory.item_set.all %}
<img src="{{ item.itemimage_set.first.image.url }}">
{% endfor %}
because I need to access item.itemimage_set when looping through subcateogry.item_set, and this is a much worse problem than before, because it raises 19 repetition in loading itemimage
This template is rendered by a ListView
class CategoryListView(ListView):
'''display a list of items'''
model = Category
# paginate_by = 1
template_name = 'boutique/show_category.html'
context_object_name = 'category_shown'
def get_queryset(self):
qs = super().get_queryset().get_categories_with_item()
self.gender = self.kwargs.get('gender') # reuse in context
gender = self.gender
request = self.request
# fetch filter-form data
self.category_selected = request.GET.get('category_selected')
self.brand_selected = request.GET.get('brand_selected')
self.min_price = request.GET.get('min_price')
self.max_price = request.GET.get('max_price')
if gender == 'women':
self.gender_number = 1
elif gender == 'men':
self.gender_number = 2
else:
raise Http404
get_category_selected = Category.objects.filter(
gender=self.gender_number, name__iexact=self.category_selected).first()
category_selected_pk = get_category_selected.pk if get_category_selected else None
get_subcategory_selected = SubCategory.objects.filter(
category__gender=self.gender_number, name__iexact=self.category_selected).first()
subcategory_selected_pk = get_subcategory_selected.pk if get_subcategory_selected else None
category_pk = category_selected_pk if category_selected_pk else self.kwargs.get(
'category_pk')
subcategory_pk = subcategory_selected_pk if subcategory_selected_pk else self.kwargs.get(
'subcategory_pk')
# print('\nself.kwargs:\n', gender, category_pk, subcategory_pk)
if gender and not category_pk and not subcategory_pk:
qs = qs.get_categories_by_gender(gender)
# print('\nCategoryLV_qs_gender= ', '\n', qs, '\n', gender, '\n')
return qs
elif gender and category_pk:
qs = qs.filter(pk=category_pk)
# print('\nCategoryLV_qs_category= ', '\n', qs, '\n')
return qs
elif gender and subcategory_pk:
qs = SubCategory.objects.annotate(Count('item')).exclude(
item__count=0).filter(pk=subcategory_pk)
self.context_object_name = 'subcategory_shown'
# print('\nCategoryLV_qs_sub_category= ', '\n', qs, '\n')
return qs
def get_validated_cats(self):
categories_validated = []
subcategories_validated = []
items_validated = []
brand_selected = self.brand_selected
min_price = self.min_price
if min_price == '' or min_price is None:
min_price = 0
max_price = self.max_price
if max_price == '' or max_price is None:
max_price = 999999
for item in Item.objects.select_related('category', 'subcategory', 'tag').filter(category__gender=self.gender_number):
if int(min_price) <= item.final_price < int(max_price):
if brand_selected is None or brand_selected == 'бренд' or item.brand.name == brand_selected:
items_validated.append(item)
if item.category not in categories_validated:
categories_validated.append(item.category)
if item.subcategory not in subcategories_validated:
subcategories_validated.append(item.subcategory)
return categories_validated, subcategories_validated, items_validated
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['brands'] = Brand.objects.all()
cat_valid, subcat_valid, items_valid = self.get_validated_cats()
context['filter_context'] = {
'gender': self.gender,
'gender_number': self.gender_number,
'category_selected': self.category_selected,
'brand_selected': self.brand_selected,
'min_price': self.min_price,
'max_price': self.max_price,
'categories_validated': cat_valid,
'subcategories_validated': subcat_valid,
'items_validated': items_valid,
}
# print(context)
return context
You should use prefetch_related to prefetch "itemimage_set" so that every time you access item.itemimage_set.all you get the cached result
item = get_object_or_404(Item.objects.prefetch_related('itemimage_set'), pk=pk)
For a DetailView
class ItemDetailView(DetailView):
model = Item
queryset = Item.objects.prefetch_related('itemimage_set')

Django search form for filtering

I am currently doing a search using forms
This is my views.py
class HomeView(generic.ListView):
model = Consultant
template_name = 'sogeti/home.html'
def get_queryset(self):
query = self.request.GET.get('q')
if query:
return Consultant.objects.filter(
Q(first_name__icontains=query) |
Q(last_name__icontains=query) |
Q(group__title_techGroup__contains=query) |
Q(practices__title_practice__contains=query)
)
else:
return Consultant.objects.all()
and this is my home.html
<form action="" method="get" class="form-inline">
<input type="text" name="q" placeholder="Enter Keyword" value="{{ request.GET.q }}" class="form-control">
<select name="filter" class="form-control">
<option value="all">All</option>
<option value="people">People</option>
<option value="certification">Certification</option>
<option value="skillset">Skillset</option>
</select>
<input type="submit" value="Search" class="btn btn-default">
</form>
<ol style="padding-left: 15px">
{% for consultant in object_list %}
<li>
{{ consultant.first_name }}, {{ consultant.last_name }} </br>
Technology Group: {{ consultant.group }} </br>
Primary Practice: {{ consultant.practices }}
<hr style="margin-left: 0">
</li>
{% endfor %}
</ol>
My first problem is that when it tries to search something (Eg: bla) that is not in my database, it returns a blank screen. Nothing at all. Tried searching but could not get any answers.
My second problem is how am I able to specify my search using HTML select and options to filter. As you can see from my home.html I have the tag with option value but no idea how to utilize it for Django.
Thank you so much for your help! Really appriciate it.
About first issue, you actually can double check the object_list before iterate over it, e.g:
{% if object_list %}
<ul>
{% for item in object_list %}
<p>{{item.value}}</p>
{% endfor %}
</ul>
{% else %}
<p>Empty!</p>
{% endif %}
If you're unable to search, then double check your query by using some tools like django-debug-toolbar to take a look at the queries.
About the second question, I recommend you to use Django Form instead, like so:
Create forms.py:
from django.forms import Form, ChoiceField, CharField
class FilterForm(Form):
FILTER_CHOICES = (
('all', 'All'),
('people', 'People'),
('certification', 'Certification'),
('skillset', 'Skillset'),
)
search = CharField(required=False)
filter_field = ChoiceField(choices=FILTER_CHOICES)
Then your view (views.py):
class HomeView(ListView):
model = Consultant
template_name = 'home.html'
def get_queryset(self):
query = self.request.GET.get('search')
filter_field = self.request.GET.get('filter_field')
# Do your filter and search here
return Consultant.objects.all()
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['form'] = FilterForm(initial={
'search': self.request.GET.get('search', ''),
'filter_field': self.request.GET.get('filter_field', ''),
})
return context
and finally your template (templates/home.html):
<form class="row">
{{ form.search }}
{{ form.filter_field }}
<input type="submit" class="btn btn-default" value="Search">
</form>
Hope that would be helpful!
For the first issue, if there is nothing in the database it is returning an empty queryset so if you include
{% if object_list %}
do stuff
{% else %}
No results match your search
{% endif %}
For the second it depends on what you're trying to do, but let's suppose it is a
specific field you're searching.
search_choice = self.request.GET.get('filter')
choice_query = Q(search_field=search_choice)
Then simply add this to your query
views.py
class HomeView(generic.ListView):
model = Consultant
template_name = 'sogeti/home.html'
def get_queryset(self):
queryset = super().get_queryset()
q = self.request.GET.get('q')
if q:
queryset = queryset.filter(
Q(first_name__icontains=q) |
Q(last_name__icontains=q) |
Q(group__title_techGroup__contains=q) |
Q(practices__title_practice__contains=q)
)
filter = self.request.GET.get('filter')
if filter == 'people':
pass
elif filter == '...':
# do sth
return queryset
html
<form action="" method="get" class="form-inline">
<input type="text" name="q" placeholder="Enter Keyword" value="{{ request.GET.q }}" class="form-control">
<select name="filter" class="form-control">
<option value="all">All</option>
{% if request.GET.filter == 'people' %}
<option value="people" selected>People</option>
{% else %}
<option value="people">People</option>
{% endif %}
{% if request.GET.filter == 'certification' %}
<option value="certification" selected>Certification</option>
{% else %}
<option value="certification">Certification</option>
{% endif %}
{% if request.GET.filter == 'skillset' %}
<option value="skillset" selected>Skillset</option>
{% else %}
<option value="skillset">Skillset</option>
{% endif %}
</select>
<input type="submit" value="Search" class="btn btn-default">
</form>
<ol style="padding-left: 15px">
{% for consultant in object_list %}
<li>
{{ consultant.first_name }}, {{ consultant.last_name }} </br>
Technology Group: {{ consultant.group }} </br>
Primary Practice: {{ consultant.practices }}
<hr style="margin-left: 0">
</li>
{% empty %}
No result match q=`<b>{{ request.GET.q }}</b>` & filter=`{{ request.GET.filter }}`
{% endfor %}
</ol>

Categories

Resources