How to make 1:N orm? use prefetch_related? - django

template.html
{% for portfolioList in portfolio_list %}
<div class="jobster-timeline-item">
<div class="jobster-timeline-cricle">
<i class="far fa-circle"></i>
</div>
<div class="jobster-timeline-info">
<div class="dashboard-timeline-info">
<div class="dashboard-timeline-edit">
<ul class="list-unstyled d-flex">
<li><a class="text-right" href="{% url 'MyPortfolioUpdate' post_id=portfolioList.idx %}" role="button"> <i class="fas fa-pencil-alt text-info mr-2"></i> </a></li>
<li><i class="far fa-trash-alt text-danger"></i></li>
</ul>
</div>
<span class="jobster-timeline-time">{{portfolioList.start_date|date:"Y년 m월"}} ~ {{portfolioList.end_date|date:"Y년 m월"}}</span>
<h6 class="mb-2">{{portfolioList.subject}}</h6>
<span>-{{portfolioList.client_name}}</span>
<p class="mt-2">{{portfolioList.content|linebreaksbr}}</p>
<div class="row">
{% for portfolioImgList in portfolio_img_list %}
{% if portfolioImgList.portfolio_idx.idx == portfolioList.idx %}
<div class="col-md-6">
<img class="img-fluid" src="{{portfolioImgList.file.url}}" alt="" >
</div>
{% endif %}
{% endfor %}
</div>
</div>
</div>
</div>
{% endfor %}
view.py
q = Q()
q &= Q(user_idx = request.user.id)
portfolio_list = UserPortfolio.objects.filter(q).order_by('-idx')
q = Q()
q &= Q(portfolio_idx__user_idx = request.user.id)
portfolio_img_list = UserPortfolioFile.objects.filter(q).order_by('-idx')
return render(request, 'account/setting/portfolio_list.html', {"portfolio_list":portfolio_list, "portfolio_img_list":portfolio_img_list}
model.py
class UserPortfolio(models.Model):
idx = models.AutoField(primary_key=True)
user_idx = models.ForeignKey(
User,
db_column='user_idx',
on_delete=models.CASCADE
)
subject = models.CharField(max_length=255)
client_name = models.CharField(max_length=255)
client_service = models.CharField(max_length=255)
client_category = models.CharField(max_length=255)
start_date = models.DateTimeField()
end_date = models.DateTimeField()
content = models.TextField()
write_date = models.DateTimeField(auto_now = True)
update_date = models.DateTimeField(auto_now = True)
is_file = models.CharField(max_length=1)
class Meta:
managed = False
db_table = 'account_user_portfolio'
def portfolio_upload_to(instance, filename):
nowDate = datetime.now().strftime("%Y/%m/%d")
return '/'.join([str(instance.portfolio_idx.user_idx), instance.folder , nowDate, filename])
class UserPortfolioFile(models.Model):
idx = models.AutoField(primary_key=True)
portfolio_idx = models.ForeignKey(
UserPortfolio,
db_column='portfolio_idx',
on_delete=models.CASCADE
)
folder = 'portfolio'
file = models.ImageField(upload_to=portfolio_upload_to)
class Meta:
managed = False
db_table = 'account_user_portfolio_file'
This is result.
I want to use join orm in view.py. not like this. used 'if' in template.py
How can I do this? And I tried use join sql. but It was not worked.
I used prefetch_related this. but Its result was same as now.
And I think This is maybe good. doesn't?
Please answer something and thanks to every one

You can enumerate with .userportfoliofile_set.all:
{% for portfolioImgList in portfolioList.userportfoliofile_set.all %}
…
{% endfor %}
this will make a query to only retrieve the UserPortfolioFiles related to the portfolioList object.
In the view you can work with .prefetch_related(…) [Django-doc] to fetch all related items with one query, so:
def some_view(request):
portfolio_list = UserPortfolio.objects.filter(
user_idx=request.user.id
).prefetch_related('userportfoliofile_set').order_by('-idx')
return render(request, 'account/setting/portfolio_list.html', {'portfolio_list':portfolio_list})

Related

Django Display count of database entries related via foreign key

I have two models, ProjectNotes and ProjectNoteComments. ProjectNoteComments are related to ProjectNotes via a foreign key. I want to display the number of comments each note has on a listview. I am just learning Django and so far I have not been able to figure out how to retrieve and display the comment count.
My view:
(I do import count)
class ProjectNotesList(ListView):
model = ProjectNotes
template_name = 'company_accounts/project_notes.html'
comments = ProjectNotes.comments
def related_project(self, **kwargs):
project = get_object_or_404(Project, id=self.kwargs.get('pk'))
notes = ProjectNotes.objects.all
return notes
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super().get_context_data(**kwargs)
context['project'] = get_object_or_404(Project, id=self.kwargs.get('pk'))
return context
commentscount = ProjectNotes.objects.annotate(num_comments=Count('comments'))
My template:
{% extends 'base.html' %}
{% block content %}
<div class="section-container container">
<h1>Notes for {{ project }}</h1>
{% if project.notes.all %}
{% for note in project.notes.all %}
<div class ="projectnotes-entry">
<div class="col-sm-8">
<div class="row-sm-6">
<div class="card mb-2">
<div class="card-body">
<div class="card-title">{{ note.title }}</div>
<div class="card-text">{{ note.body | safe | truncatewords:"20"|linebreaks }}
read more</div>
</div>
</div>
</div>
</div>
</div>
<h2>comments count</h2>
{{ commentscount }}
{% endfor %}
{% else %}
<p>No notes have been have been added yet.</p>
{% endif %}
</div>
{% endblock content %}
The models:
class ProjectNotes(models.Model):
title = models.CharField(max_length=200)
body = tinymce_models.HTMLField()
date = models.DateField(auto_now_add=True)
project = models.ForeignKey(Project, default=0, blank=True, on_delete=models.CASCADE, related_name='notes')
def __str__(self):
return self.title
class ProjectNoteComments(models.Model):
body = tinymce_models.HTMLField()
date = models.DateField(auto_now_add=True)
projectnote = models.ForeignKey(ProjectNotes, default=0, blank=True, on_delete=models.CASCADE, related_name='comments')
Short version:
{{ note.comments.all.count }} # possibly works also without 'all' but can't check right now
I've just answered similar problem with simple explanation of relationships.
https://stackoverflow.com/a/70955851/12775662
Read official docs, it's really rewarding. https://docs.djangoproject.com/en/4.0/topics/db/models/#relationships

How to slice items generated from a forloop with conditionals in django

I have a list of categories as well as a list of products my template is in such a manner that it has category sections each with a display of products that belong to said categories. I created a for loop for categories so as to easily display category sections for each category I create. I then went on to create a forloop for products within the category forloop with a condition so as to match products with their actual category before they are displayed under their category section. how can I slice the resulting products to limit the number of products shown
Models.py
class Category(models.Model):
name = models.CharField(max_length=120)
image_263x629 = models.ImageField(upload_to='cat_imgs')
image_263x629_2 = models.ImageField(upload_to='cat_imgs')
image_263x629_3 = models.ImageField(upload_to='cat_imgs')
img_array = [image_263x629, image_263x629_2, image_263x629_3]
description = models.CharField(max_length=250)
def __str__(self):
return self.name
class SubCategory(models.Model):
name = models.CharField(max_length=200)
description = models.CharField(max_length=300)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
def __str__(self):
return self.name
#def get_absolute_url(self):
# return reverse('subcat_detail', args=[str(self.id)])
class Product(models.Model):
name = models.CharField(max_length=120)
price = models.FloatField()
image_182x182 = models.ImageField(upload_to='pdt_imgs/')
image_1200x1200 = models.ImageField(upload_to='pdt_imgs/alt_imgs/')
image_600x600 = models.ImageField(upload_to='pdt_imgs/alt_imgs/')
image_600x600_2 = models.ImageField(upload_to='pdt_imgs/alt_imgs/')
image_300x300 = models.ImageField(upload_to='pdt_imgs/alt_imgs/')
img_array = [image_1200x1200, image_600x600, image_600x600_2]
sku = models.IntegerField()
available = models.BooleanField(default=True)
discount = models.IntegerField(default = 0)
description = models.CharField(max_length=120, blank=True, null=True)
brand = models.CharField(max_length=120, blank=True, null=True)
category = models.ForeignKey(SubCategory, on_delete=models.CASCADE)
seller = models.ForeignKey(Seller, on_delete=models.CASCADE)
Views
class HomePageView(ListView):
model = SubCategory
template_name = 'home.html'
queryset = SubCategory.objects.all()
def get_context_data(self, **kwargs):
context = super(HomePageView, self).get_context_data(**kwargs)
context['products'] = Product.objects.all()
context['pdts'] = Product.objects.order_by('?')[:12]
context['categories'] = Category.objects.all()
context['subcategories'] = SubCategory.objects.all()
return context
Template
{% for category in categories %}
<div class="ps-block--products-of-category">
<div class="ps-block__categories">
<h3>{{ category.name }}</h3>
<ul>
{% for subcategory in subcategories %}
{% if subcategory.category.name == category.name %}
<li>{{ subcategory.name }}</li>
{% endif %}
{% endfor %}
</ul><a class="ps-block__more-link" href="{% url 'cat_detail' category.id %}">View All</a>
</div>
<div class="ps-block__slider">
<div class="ps-carousel--product-box owl-slider" data-owl-auto="true" data-owl-loop="true"
data-owl-speed="7000" data-owl-gap="0" data-owl-nav="true" data-owl-dots="true" data-owl-item="1"
data-owl-item-xs="1" data-owl-item-sm="1" data-owl-item-md="1" data-owl-item-lg="1" data-owl-duration="500"
data-owl-mousedrag="off">
<img src="{{ category.image_263x629.url }}" alt="">
<img src="{{ category.image_263x629_2.url }}" alt="">
<img src="{{ category.image_263x629_3.url }}" alt="">
</div>
</div>
<div class="ps-block__product-box">
{% for product in products %}
{% if product.category.category.name == category.name %}
<div class="ps-product ps-product--simple">
<div class="ps-product__thumbnail"><a href="{% url 'pdt_detail' product.id %}"><img src="{{ product.image_300x300.url }}"
alt=""></a>
{% if product.discount > 0 %}
<div class="ps-product__badge">-{{ product.discount }}%</div>
{% endif %}
{% if product.available == False %}
<div class="ps-product__badge out-stock">Out Of Stock</div>
{% endif %}
</div>
<div class="ps-product__container">
<div class="ps-product__content" data-mh="clothing"><a class="ps-product__title"
href="{% url 'pdt_detail' product.id %}">{{ product.name }}</a>
<div class="ps-product__rating">
<select class="ps-rating" data-read-only="true">
<option value="1">1</option>
<option value="1">2</option>
<option value="1">3</option>
<option value="1">4</option>
<option value="2">5</option>
</select><span>01</span>
</div>
<p class="ps-product__price sale">UGX{{ product.price }}</p>
</div>
</div>
</div>
{% endif %}
{% endfor %}
</div>
</div>
{% endfor %}
Please do not filter in the template. You should filter in the view. A template implements rendering logic, not business logic*.
You can filter and slice in the view with:
def my_view(request):
# …
products = Product.objects.filter(category__name='specified category')[:10]
context = {
'products': products
}
return render(request, 'my_template.html', context)
This is not only the place where filtering belongs, it is also more efficient since we here will filter and slice on the database side. Typically a database can do this more efficient, and it furthermore limits the bandwidth from the database to the Django/Python layer.
Note (based on #SLDem's comment):
If you aim to filter children, you make use of a Prefetch object [Django-doc]. Indeed, imagine that we have a QuerySet of Categorys and we want to only retain Products that are available, we can use:
from django.db.models import Prefetch
categories = Category.objects.prefetch_related(
Prefetch(
'product_set',
Product.objects.filter(available=True),
to_attr='available_products'
)
)
then in the template we can render this with:
{% for category in categories %}
{% for product in category.available_products %}
…
{% endfor %}
{% endfor %}

Django template loop through items with parent ID or PK

I'm trying to set up magnific popup on django.
My goal is to have one main picture in the homepage overview gallery view, which when clicked, would open a popup with the related images from the same photoshoot i.e. images with the same ID or PK.
I tried to apply the following approach
but i just cannot get it to work, maybe someone could help me out in this
My models.py
class Item(models.Model):
name = models.CharField(blank=False, max_length=200)
category = models.ForeignKey(Category, blank=True, null=True, on_delete=models.SET_NULL)
order = models.IntegerField(blank=True, null=True)
active = models.BooleanField(blank=True, default=False)
objects = models.Manager()
class Meta:
verbose_name_plural = 'items'
def __str__(self):
return self.name
class ItemImage(models.Model):
image = ProcessedImageField(
blank=True,
null=True,
processors=[ResizeToFit(width=1680, upscale=False)],
format='JPEG',
options={'quality':90})
order = models.IntegerField(blank=True, null=True)
main = models.BooleanField(blank=True, default=False)
cover = models.BooleanField(blank=True, default=False)
item = models.ForeignKey(Item, related_name='items', blank=True, null=True, on_delete=models.SET_NULL)
objects = models.Manager()
class Meta:
verbose_name_plural = 'item images'
Views.py
def portraits(request):
port = ItemImage.objects.filter(item__category__slug='portraits', item__active=True, main=True,).order_by('item__order')
portall = ItemImage.objects.filter(item__category__slug='portraits', item__active=True).order_by('item__order')
context = {
'main_portraits': port,
'all_portraits': portall
}
return render(request, 'gallery/portraits.html', context)
and Template:
{% block content %}
<div class="grid">
{% for pic in main_portraits %}
<div class="item">
<div class="item">
<div class="outer-text">
<div class="text">
{{ pic.item.name }}
<p>Click to view gallery</p>
</div>
</div>
<a><img class="lazy" alt=""
sizes="(min-width:1400px) 1220px
(min-width:1000px) 1000px,
(min-width:500px) 700px,
(min-width:320px) 420px,
280px"
srcset="{{ pic.image_xs.url }} 280w,
{{ pic.image_s.url }} 420w,
{{ pic.image_m.url }} 700w,
{{ pic.image_l.url }} 1000w,
{{ pic.image_xl.url }} 1220w" />
</a> {{ pic.item.pk }}
</div>
<div class="lazy">
{% for p in all_portraits %}
{% endfor %}
</div>
</div>
{% endfor %}
</div>
{% endblock %}
I have set
z.item.pk
just as a test, to see if any of my manipulations result in the pk's to bunch up. For example the first for-loop returns a picture with PK "24", I need for the second for-lop to return only images with the same PK; and so for every image. I think the answer might be connected with _set.all function, just like in the related question above, but I cant seem to get it to work in my case. Feels like I'm missing something here.
current output:
<div class="grid">
<div class="item">
<div class="item">
<div class="outer-text">
<div class="text">
Palagā tītā
<p>Click to view gallery</p>
</div>
</div>
<a><img class="lazy" alt=""
sizes="(min-width:1400px) 1220px
(min-width:1000px) 1000px,
(min-width:500px) 700px,
(min-width:320px) 420px,
280px"
srcset="/media/CACHE/images/IMG_8329_3Vi8mYO_GD621ql/958ba5dbee5efe28fd2f5054b8f819e1.jpg 280w,
/media/CACHE/images/IMG_8329_3Vi8mYO_GD621ql/02d12ca7f0633fee2fc762cf96f7889e.jpg 420w,
/media/CACHE/images/IMG_8329_3Vi8mYO_GD621ql/ba5fa6633e92a288e3b2f47a713d64c2.jpg 700w,
/media/CACHE/images/IMG_8329_3Vi8mYO_GD621ql/fe0d559fef5b02434c43f841005d4961.jpg 1000w,
/media/CACHE/images/IMG_8329_3Vi8mYO_GD621ql/96d0e52dff14d1bc4b60bbec674565db.jpg 1220w" />
</a> 24
</div>
<div class="lazy">
</div>
</div>
You need prefiltered querysets containing the related images for every main image before handing over to the template.
def portraits(request):
ports = ItemImage.objects.filter(item__category__slug='portraits', item__active=True, main=True,).order_by('item__order')
for p in ports:
# You may not need the item__category__slug filter
# if there are only images of the same category
# associated with an item.
# Also, if you want to exclude the main image
# from the set of related images, you need to add the filter
# main=False
p.related_images = ItemImage.objects.filter(item__category__slug='portraits', item__id=p.item.id)
context = {
'main_portraits': ports,
}
return render(request, 'gallery/portraits.html', context)
Then you can loop over main_portraits in the template, and get the related images for each main image in a nested loop:
{% for mainp in main_portraits %}
{% for im in mainp.related_images %}
{# do something with the related images #}
{% endfor %}
{% endfor %}
You can break down the models like this it will make the querying easier.
# models.py
class Item(mdoels.Model):
name = models.CharField(blank=False, max_length=200)
category = models.ForeignKey(Category, blank=True, null=True, on_delete=models.SET_NULL)
...
display_image = models.ProcessedImageField(...)
class ItemImage(models.Model):
item = models.ForeignKey(Item, related_name='images', blank=True, null=True, on_delete=models.SET_NULL)
image = models.ProcessedImageField(...)
...
#views.py
def portraits(request):
items = Item.objects.filter(category__slug='portraits', active=True)
return render(request, 'gallery/portraits.html', context={items: items})
#template
{% for item in items %}
<h1> {{item.name}} </h1>
<img src={{item.display_image}} />
{% for item_image in item.images.all %}
<img src={{item_image.image}} />
{% endfor %}
{% endfor %}

After using prefetch_related still facing n+1

I am facing multiple duplicate db call even after I am using prefetch_related.
Here is my views:
def index(request):
product_qs = Product.objects.filter(shop_type='Grocery', featured=True, hide=False)
product_list = product_qs.prefetch_related('image', 'size_list').order_by('ranking')
products = terminator(request, product_list, 25) // reusable paginator!
context = {'categories': categories, 'products': products}
return render(request, 'grocery/index.html', context)
Here is my models:
class ProductImage(models.Model):
image = models.ImageField(upload_to='shop/product')
color = models.BooleanField(default=False)
image_thumbnail_index = ImageSpecField(source='image',
processors=[ResizeToFill(308, 412)],
format='JPEG',
options={'quality': 100})
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Product(models.Model):
shop_type = models.CharField(choices=shop_options, max_length=40)
name = models.CharField(max_length=200, unique=True)
image = models.ManyToManyField(ProductImage)
category = TreeForeignKey('Category', null=True, blank=True, on_delete=models.PROTECT)
slug = models.SlugField(max_length=200, db_index=True)
featured = models.BooleanField(default=False)
size = models.BooleanField(default=False)
size_list = models.ManyToManyField(SizeList)
available = models.BooleanField(default=True) # out of stock ?
hide = models.BooleanField(default=False) # archive a product
ranking = models.PositiveIntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
and the template:
<figure class="product-image-container">
<a href="#" class="product-image">
<img src="{{ product.image.first.image_thumbnail_index.url }}" alt="{{ products.name }}">
</a>
{% trans 'Quickview' %}
</figure>
<div class="product-details">
<h2 class="product-title">
<a href="/grocery/product/{{ product.slug }}">
{% if LANGUAGE_CODE == 'en' %}
{{ product.name }}
{% else %}
{{ product.name_bn }}
{% endif %}
</a>
</h2>
<div class="price-box">
<span class="product-price">
{% if LANGUAGE_CODE == 'en' %}
৳ {{ product.price }}
{% else %}
৳ {{ product.price_bn }}
{% endif %}
</span>
</div><!-- End .price-box -->
Screenshot of sql calls:
Since you are already prefetching the ProductImage objects you may use the of the prefetched objects: {% with product.image.all|first as image %}{{ image.image_thumbnail_index.url }}(% endwith %}. (While calling first() does an extra query using LIMIT 1 at the end.)

Display a calculated value from models in a template in Django

I want to show the calculated discount under every product. The code below has no errors, but it does not display the value.
models.py:
from django.db import models
# Create your models here.
CATEGORIES = (
('Electronics', 'Electronics'),
('Clothing', 'Clothing'),
)
class Products(models.Model):
Image = models.FileField()
ProductName = models.CharField(max_length = 250, default='')
Brand = models.CharField(max_length = 250, default='')
OriginalPrice = models.IntegerField(default = '')
Price = models.IntegerField(default = '')
Category = models.CharField(max_length = 250, choices = CATEGORIES)
class Meta:
verbose_name = 'Product'
verbose_name_plural = 'Products'
def DiscountCalc(self):
Discount = (Price/OriginalPrice) * 100
return self.Discount
def __str__ (self):
return self.ProductName
This is the template
index.html:
{% for product in AllProducts %}
<article class="product col-sm-3">
<a href="#" class="prodlink">
<img src="{{ product.Image.url }}" class="prodimg img-responsive">
<p class="prodname">{{ product.ProductName }}</p>
<span class="origprice">₹{{ product.OriginalPrice }}</span>
<span class="price">₹{{ product.Price }}</span>
<div class="discount">
<p>{{ product.Discount }}% off</p>
</div>
</a>
</article>
{% endfor %}
you need to make a property :
#property
def Discount(self):
return (self.Price/self.OriginalPrice) * 100
So now you can use product.Discount :)
Docs here Django model-methods
1)Defining a local variable and Returning a class variable will return null
2)You have to have the same name in models and template
models.py
def DiscountCalc(self):
Discount = (Price/OriginalPrice) * 100
return Discount
index.html
{% for product in AllProducts %}
<article class="product col-sm-3">
<a href="#" class="prodlink">
<img src="{{ product.Image.url }}" class="prodimg img-responsive">
<p class="prodname">{{ product.ProductName }}</p>
<span class="origprice">₹{{ product.OriginalPrice }}</span>
<span class="price">₹{{ product.Price }}</span>
<div class="discount">
<p>{{ product.DiscountCalc }}% off</p>
</div>
</a>
</article>
{% endfor %}