def allProductCat(request, c_slug=None):
c_page = None
products_list = None
if c_slug is not None:
c_page = get_object_or_404(Category, slug=c_slug)
products_list = Product.objects.all().filter(category=c_page, available=True)
else:
products_list = Product.objects.all().filter(available=True)
paginator = Paginator(products_list, 6)
try:
page = int(request.GET.get('page', '1'))
except:
page = 1
try:
products = paginator.page(page)
except(EmptyPage, InvalidPage):
products = paginator.page(paginator.num_pages)
return render(request, "category.html", {'category': c_page, 'product': products})
// Code for Html //
<div class="mx-auto">
{% if product.paginator.num_page %}
<hr>
<div class="text-center">
{% for pg in product.paginator.page_range %}
{{pg}}
{% endfor %}
</div>
{% endif %}
</div>
when i add all these codes pagination doesnt shows up anything when i type the links to next page manually its working perfectly i dont understand whats wrong in this code, also these div doesnt shows anything inside it when i type anything...
====== views.py =======
from django.core.paginator import Paginator
def HomeView(request):
show_data = VehicleModel.objects.all() # Queryset For pagiantion
# Pagination code start
paginator = Paginator(show_data, 3, orphans=1)
page_number = request.GET.get('page')
show_data = paginator.get_page(page_number)
# Pagination code end
context = {'page_number':page_number}
return render(request,'dashboard.html',context)
======= in HTML ==========
# <!-- Pagination Block with page number -->
<div class="container mt-5">
<div class="row float-right ">
<span class="m-0 p-0">
{% if show_data.has_previous %} # <!-- For Previous Button -->
<a class="btn btn-outline-info" href="?page={{show_data.previous_page_number}}&ok=#ok">Previous</a>
{% endif %}
<span>{% for pg in show_data.paginator.page_range %} # <!-- For Page Numbers Buttons -->
{% if show_data.number == pg %}
<a href="?page={{pg}}" class="btn btn-sm btn-primary">
<span class="badge">{{pg}}</span>
</a>
{% else %}
<a href="?page={{pg}}" class="btn btn-sm btn-secondary">
<span class="badge">{{pg}}</span>
</a>
{% endif %}
{% endfor %}</span>
{% if show_data.has_next %} # <!-- For Next Button -->
<a class="btn btn-outline-info" href="?page={{show_data.next_page_number}}&ok=#ok">Next</a>
{% endif %}
</span>
</div>
</div>
Related
So, I want to be able to paginate data that is coming in from an API, but I am not sure how to. I have looked at pagination with Flask and almost everyone is using SQLAlchemy and a Database to paginate, so I am kind of stuck right now. I had tried everything but I havent been lucky, so I hope you guys can help me with this matter. Thanks a lo.
This is the code that gets me the data from the API:
from flask import Flask
from flask import render_template
import requests
from flask import request
app = Flask(__name__)
api = '12589634-45tg33ww8633f1cdfrre3345'
url = 'https://pixabay.com/api/'
#app.route("/", methods=['POST', 'GET'])
def homePage():
per_page = 18
page = request.args.get('page', 0, type=int)
query = request.args.get('s', 'cats')
payload = {'key': api, 'q': query, 'per_page': per_page, 'page': page}
response = requests.get(url, params=payload)
data = response.json()
results = data.get('hits')
images = results
return render_template('home_page.html', images=images)
Here is my template:
<pre>
{% extends 'base.html' %}
{% block content %}
<div class="columns is-multiline is-mobile">
{% for image in images %}
<div class="column is-full-mobile is-half-tablet is-one-third-desktop">
<div class="card">
<div class="card-image">
<figure class="image is-4by3">
<img src="{{image.webformatURL}}" alt="{{image.tags}}">
</figure>
</div>
<div class="card-content">
<div class="media">
<div class="media-left">
<figure class="image is-48x48">
{% if image.userImageURL %}
<img src="{{image.userImageURL}}" alt="{{image.user}}">
{% else %}
<img src="{{url_for('static', filename='images/avatar/avatar.png')}}" alt="no user image">
{% endif %}
</figure>
</div>
<div class="media-content">
<p class="title is-5">Uploaded by</p>
<p class="subtitle is-6">#{{image.user}}</p>
</div>
</div>
<div class="content">
<p class="title is-6">Likes: {{image.likes}}</p>
<p class="title is-6">Type: {{image.type}}</p>
<p class="title is-6">Views: {{image.views}}</p>
<a href="{{image.largeImageURL}}" class="button is-dark is-fullwidth" target="_blank"
rel="noopener nofollow">View
full</a>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
<nav class="pagination" role="navigation" aria-label="pagination">
<a class="pagination-previous" title="This is the first page" disabled>Previous</a>
<a class="pagination-next">Next page</a>
<ul class="pagination-list">
<li>
<a class="pagination-link is-current" aria-label="Page 1" aria-current="page">1</a>
</li>
<li>
<a class="pagination-link" aria-label="Goto page 2">2</a>
</li>
<li>
<a class="pagination-link" aria-label="Goto page 3">3</a>
</li>
</ul>
</nav>
{% endblock content %}
</pre>
I thin you can make a custom paginator. A simple example to it.
[...]
ques = images # the images variable you are passing.
page_num = request.args.get("page_num")
no_of_ques = request.args.get("no_of_ques")
# some error handling
try:
no_of_ques = int(no_of_ques)
except:
no_of_ques = 10
if page_num == None:
page_num = 0
try:
page_num = int(page_num)
except:
abort(404)
# logic to get the last page
last = math.ceil(len(ques)/no_of_ques) - 1
# logic to get images on a page
ques1 = ques[page_num*no_of_ques:(page_num+1)*no_of_ques] # paginatted
# making the links for a tagsg
if last == 0:
next = "#"
prev = "#"
elif page_num == 0:
next = f"/?page_num={page_num+1}"
prev = "#"
elif page_num == last:
next = "#"
prev = f"/?page_num={page_num-1}"
else:
next = f"/?page_num={page_num+1}"
prev = f"/?page_num={page_num-1}"
if (page_num > last or not str(page_num).isnumeric():
abort(404)
return render_template("html_file.html",images=ques1,next=next,prev=prev)
# ques1 has the number of images on a page.
in html make two a tag
<a class="btn btn-primary" src="{{prev}}" >← prev</a>
<a class="btn btn-primary" src={{next}} >Next →</a>
This might not be the best solution but will do the work.
I am trying to create a edit form to update the database using Django model Forms but the problem is that edit form part of the sizeProductMap.html page is not rendering when edit form (sizeProductMap_edit) request is made.
My models are as shown below.
models.py
class Product(models.Model):
prod_ID = models.AutoField("Product ID", primary_key=True)
prod_Name = models.CharField("Product Name", max_length=30, null=False)
prod_Desc = models.CharField("Product Description", max_length=2000, null=False)
prod_Price = models.IntegerField("Product Price/Piece", default=0.00)
prod_img = models.ImageField("Product Image", null=True)
def __str__(self):
return "{}-->{}".format(self.prod_ID,
self.prod_Name)
class Size(models.Model):
size_id = models.AutoField("Size ID", primary_key=True, auto_created=True)
prod_size = models.CharField("Product Size", max_length=20, null=False)
def __str__(self):
return "{size_id}-->{prod_size}".format(size_id=self.size_id,
prod_size=self.prod_size)
class SizeProductMapping(models.Model):
size_p_map_id = models.AutoField("Size & Product Map ID", primary_key=True, auto_created=True)
size_id = models.ForeignKey(Size, null=False, on_delete=models.CASCADE, verbose_name="Size ID")
prod_id = models.ForeignKey(Product, null=False, on_delete=models.CASCADE, verbose_name="Product Id")
def __str__(self):
return ".`. {}_____{}".format(self.size_id,
self.prod_id)
This is the form I used to add and edit the model.
forms.py
from django import forms
from user.models import SizeProductMapping
class SizeProductMapForm(forms.ModelForm):
class Meta:
model = SizeProductMapping
fields = ['size_id', 'prod_id']
Here is the view I created to add ,update and delete the record.
views.py
def sizeProductMap(request):
form = SizeProductMapForm(request.POST, request.FILES)
if request.method == 'POST':
if form.is_valid():
form.save()
return redirect("/admin1/sizeProductMap/")
else:
sizeProductMap_show = SizeProductMapping.objects.all()
# start paginator logic
paginator = Paginator(sizeProductMap_show, 3)
page = request.GET.get('page')
try:
sizeProductMap_show = paginator.page(page)
except PageNotAnInteger:
sizeProductMap_show = paginator.page(1)
except EmptyPage:
sizeProductMap_show = paginator.page(paginator.num_pages)
# end paginator logic
return render(request, 'admin1/sizeProductMap.html', {'sizeProductMap_show': sizeProductMap_show, 'form': form})
def sizeProductMap_delete(request, id):
sizeProductMap_delete = SizeProductMapping.objects.filter(size_p_map_id=id)
sizeProductMap_delete.delete()
return redirect('/admin1/productSizeMap')
def sizeProductMap_edit(request, id):
instance = SizeProductMapping.objects.get(size_p_map_id=id)
form = SizeProductMapForm(instance=instance)
if request.method == 'POST':
form = SizeProductMapForm(request.POST, instance=instance)
if form.is_valid():
form.save()
return redirect('/admin1/sizeProductMap')
return render(request, 'admin1/sizeProductMap.html', {'form': form})
This is my urls.
urls.py
from django.urls import path
from admin1 import views
urlpatterns = [
path('sizeProductMap/', views.sizeProductMap, name="admin-size-product-map"),
path('sizeProductMap_delete/<int:id>', views.sizeProductMap_delete, name="admin-size-product-map-delete"),
path('sizeProductMap_edit/<int:id>', views.sizeProductMap_edit, name="admin-size-product-map-edit"),
]
This is the Html page where I want to display the form according to the page request.
sizeProductMap.html
{% extends 'admin1/layout/master.html' %}
{% block title %}Size Product Map{% endblock %}
{% block main %}
<h1>
<center>Size Product Map</center>
</h1>
<div class="container">
<div class="row">
<div class="col-lg-2"></div>
<div class="col-lg-10">
{% if sizeProductMap_show %}
<button type="button" class="btn btn-primary mt-2" data-toggle="modal" data-target="#modal-primary">Add
Size Product Mapping
</button>
<div class="modal fade" id="modal-primary">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Add Size Product Mapping</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span></button>
</div>
<div class="modal-body mt-2">
<form action="{% url 'admin-size-product-map'%}" method="POST"
enctype="multipart/form-data">
{% csrf_token %}
<table border="1" class="table table-bordered border border-info">
<tr>
<th>
{{form.size_id.label_tag}}
</th>
<td>{{form.size_id}}</td>
</tr>
<tr>
<th>
{{form.prod_id.label_tag}}
</th>
<td>
{{form.prod_id}}
</td>
</tr>
</table>
<input type="Submit" name="Submit" value="Submit" class="btn btn-success w-50"><br>
<div class="modal-footer justify-content-between">
<button type="button" class="btn btn-outline-light" data-dismiss="modal">Close
</button>
</div>
</form>
</div>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
<!-- /.modal -->
<div class="container-fluid ">
<div class="row">
<div class="card mt-2 border border-secondary">
<div class="card-header">
<h3 class="card-title ">Size Product Map Table</h3>
</div>
<!-- /.card-header -->
<div class="card-body">
<table class="table table-bordered border border-info">
<thead>
<tr>
<th>Size Product Mapping Id</th>
<th>Product ID</th>
<th>Size ID</th>
<th>Action</th>
</tr>
</thead>
<tbody class="justify-content-center">
{% for x in sizeProductMap_show %}
<tr>
<td>{{x.size_p_map_id}}</td>
<td>{{x.prod_id}}</td>
<td>{{x.size_id}}</td>
<td><a href="{% url 'admin-size-product-map-edit' x.size_p_map_id %}"
class="btn btn-outline-primary mt-2"><i
class="fa fa-pencil-square-o" aria-hidden="true"></i></a>
<a href="{% url 'admin-size-product-map-delete' x.size_p_map_id %}"
class="btn btn-outline-danger mt-2"><i
class="fa fa-trash" aria-hidden="true"></i></a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- /.card-body -->
<div class="card-footer clearfix ">
<ul class="pagination pagination-sm m-0 justify-content-center">
{% if sizeProductMap_show.has_previous %}
<li class="page-item"><a class="page-link"
href="?page={{sizeProductMap_show.previous_page_number}}">
Previous </a>
</li>
{% endif%}
{% for x in sizeProductMap_show.paginator.page_range %}
{% if sizeProductMap_show.number == x %}
<li class="page-item active"><a class="page-link" href="?page={{x}}">{{x}}</a></li>
{% else%}
<li class="page-item"><a class="page-link" href="?page={{x}}">{{x}}</a></li>
{% endif %}
{% endfor %}
{% if sizeProductMap_show.has_next %}
<li class="page-item"><a class="page-link"
href="?page={{sizeProductMap_show.next_page_number}}">
Next </a>
</li>
{% endif %}
</ul>
</div>
</div>
<!-- /.card -->
</div>
</div>
{% endif %}
{% if sizeProductMap_edit %}
<form action="{% url 'admin-size-product-map-edit' x.size_p_map_id %}" method="POST">
{% csrf_token %}
{{form.size_id}}
{{form.prod_id}}
</form>
{% endif %}
</div>
</div>
</div>
{% endblock %}
And if it is possible to reduce the number of line of code please also help. Thanks in advance.
I've found out the answer. There was a really a silly mistake by me.
In the sizeProductMap.html there is a mistake let me point out that:
sizeProductMap.html
{% if sizeProductMap_edit %}
<form action="{% url 'admin-size-product-map-edit' x.size_p_map_id %}" method="POST">
{% csrf_token %}
{{form.size_id}}
{{form.prod_id}}
</form>
{% endif %}
Here I am checking for instance {% if sizeProductMap_edit %} this is the wrong thing.
I have to check {% if instance %} according to my views.py.
I am at the beginner level in the Django Framework. The queryset works well in my post list. But I face a problem in pagination to the second page queryset according to the tag field in my blog post list. When I go to the second page it shows the main list of the second page, not queryset page. I don't know where the problem behind the seen...
models.py code
class Post(models.Model):
TOPICS = (
('Programming' , 'Programming'),
('C' , 'C'),
('C++' , 'C++'),
('Java' , 'Java'),
('Python' , 'Python'),
)
title = models.CharField(max_length=100, blank=False, null=False)
content = models.TextField()
tag = models.CharField(max_length=50, choices=TOPICS, blank=False, null=False, default='Programming')
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
#redirect to post_detail when some post a blog
def get_absolute_url(self):
return reverse('post_detail', kwargs={'pk':self.pk})
viwes.py code
from django.core.paginator import Paginator
from .filters import BlogFilter
def home(request):
posts = Post.objects.all().order_by('-date_posted')
postFilter = BlogFilter(request.GET, queryset=posts)
posts = postFilter.qs
paginator = Paginator(posts, 3)
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
context = {
'page_obj' : page_obj,
'postFilter' : postFilter,
}
return render(request, 'blog/index.html', context)
template code
{% extends 'blog/base.html'%}
{% load crispy_forms_tags %}
{% load static %}
{% block content %}
<section id="article">
<div class="container">
<div class="row mt-4">
<div class="col-md-8 m-auto">
<form method="get">
{{postFilter.form|crispy}}
<button class="btn btn-primary" type="submit">Search</button>
</form>
{% for i in page_obj %}
<article class="media content-section">
<a href="{% url 'user_posts' i.author.username %}">
<img class="rounded-circle article-img" src="{{i.author.profile.image.url}}" alt="">
</a>
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="{% url 'user_posts' i.author.username %}">{{ i.author }}</a>
<small class="text-muted">{{i.date_posted|date:"F d, Y"}}</small>
</div>
<h2><a class="article-title" href="{% url 'post_detail' i.id %}">{{i.title}}</a></h2>
<p class="article-content">{{i.content|linebreaks|truncatewords:50}}</p>
</div>
</article>
{% endfor %}
{% if page_obj.has_previous %}
<a class="btn btn-outline-info mb-4" href="?page=1">First</a>
<a class="btn btn-outline-info mb-4" href="?page={{ page_obj.previous_page_number}}">Previous</a>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<a class="btn btn-info mb-4" href="?page={{ num }}">{{ num }}</a>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<a class="btn btn-outline-info mb-4" href="?page={{ num }}">{{ num }}</a>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<a class="btn btn-outline-info mb-4" href="?page={{ page_obj.next_page_number }}">Next</a>
<a class="btn btn-outline-info mb-4" href="?page={{ page_obj.paginator.num_pages }}">Last</a>
{% endif %}
</div>
</div>
</div>
</section>
{% endblock %}
Hi I'm trying to develop an online store website using Django and I don't know why, but my price counter is not working. It was working all fine before I added some pagination, and now it's not adding all the values.Can anyone please help me out?
My views.py:
def cart(request):
cart = Cart.objects.all()[0]
context = {"cart":cart}
template = 'shopping_cart/cart.html'
return render(request, template, context)
def add_to_cart(request, slug):
cart = Cart.objects.all()[0]
try:
product = Product.objects.get(slug=slug)
except Product.DoesNotExist:
pass
except:
pass
if not product in cart.products.all():
cart.products.add(product)
messages.success(request, mark_safe("Product added to cart. Go to <a href='cart/'>cart</a>"))
return redirect('myshop-home')
else:
cart.products.remove(product)
messages.success(request, mark_safe("Product removed from cart"))
new_total = 0.00
for item in cart.products.all():
new_total += float(item.price)
cart.total = new_total
cart.save()
return HttpResponseRedirect(reverse('cart'))
My index.html(where I added pagination):
{% extends 'base.html' %}
{% block content %}
<h1>Products</h1>
<div class="container-md">
<div class="row">
{% for product in products %}
<div class="col">
<div class="card-deck" style="width: 18rem;">
<img src="{{ product.image_url }}" class="card-img-top" alt="...">
<div class="card-body">
<a class="card-title text-dark" href="{% url 'detail-view' product.slug %}">{{ product.name }}</a>
<p class="card-text">${{ product.price }}</p>
{% if not product.cart_set.exists %}
Add to Cart
{% else %}
Remove from Cart
{% endif %}
</div>
</div>
</div>
{% endfor %}
</div>
</div>
<div>
<ul class="pagination justify-content-center">
{% if is_paginated %}
{% if page_obj.has_previous %}
<a class="btn btn-outline-dark mb-4" href="?page=1">First</a>
<a class="btn btn-outline-dark mb-4" href="?page={{ page_obj.previous_page_number }}">Previous</a>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<a class="btn btn-dark mb-4 " href="?page={{ num }}">{{ num }}</a>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<a class="btn btn-outline-dark mb-4" href="?page={{ num }}">{{ num }}</a>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<a class="btn btn-outline-dark mb-4" href="?page={{ page_obj.next_page_number }}">Next</a>
<a class="btn btn-outline-dark mb-4" href="?page={{ page_obj.paginator.num_pages }}">Last</a>
{% endif %}
{% endif %}
</ul>
</div>
{% endblock %}
My homeview:
class homeview(ListView):
model = Product
paginate_by = 6
context_object_name = 'products'
template_name = 'index.html'
My urls.py:
urlpatterns = [
path('admin/', admin.site.urls),
path('cart/', sc_views.cart, name='cart'),
path('cart/<str:slug>/', sc_views.add_to_cart, name='add-to-cart'),
path('', include('products.urls'))
]
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')