How to data fetch by using has many through in django? - django

I am learning django. I have a simple model named customer. Here is my model:
class Year(models.Model):
year = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now=False, auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True, auto_now_add=False)
def __unicode__(self):
return self.year
def __str__(self):
return self.year
class Customer(models.Model):
name = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now=False, auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True, auto_now_add=False)
def __unicode__(self):
return self.name
def __str__(self):
return self.name
class Product(models.Model):
customer_name = models.ForeignKey(Customer, on_delete=models.CASCADE)
quantity = models.CharField(max_length=255)
year = models.ForeignKey(Year, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now=False, auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True, auto_now_add=False)
def __unicode__(self):
return self.score
def __str__(self):
return self.score
here is my view of customer:
from django.shortcuts import render, get_object_or_404, redirect
from .models import Customer, Product, Year
# Create your views here.
def home(request):
customer = Customer.objects.all
product = Product.objects.all
year = Year.objects.all().prefetch_related('product_set')
context = {'customers': customer,
'years': year,
'products': product
}
return render(request, 'customer.html', context)
Here is my customer.html
{% extends 'base.html' %}
{% block customer %}
<div class="container">
<h2>Players Table</h2>
<p>Customer with Product</p>
<table class="table">
<thead>
<tr>
<th>Year/Product</th>
{% for customer in cutomers %}
<th>{{ customer.name }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{# <tr>#}
{# <th>2011</th>#}
{# <th>633</th>#}
{# <th>424</th>#}
{# </tr>#}
{# <tr>#}
{# <th>2012</th>#}
{# <th>353</th>#}
{# <th>746</th>#}
{# </tr>#}
</tbody>
</table>
</div>
{% endblock customer %}
Now I have to generate table row which will contain year,customer and product througth customer per year.
So that How to fetch for every row that contains year, year based customer product data.
More simple,
Year has many customers and customer has one product thought year.
How to generate this. Please help me.

You don't need product or year in your context. You can use relation between your models to get this information:
views.py
from django.shortcuts import render, get_object_or_404, redirect
from .models import Customer, Product, Year
# Create your views here.
def home(request):
customer = Customer.objects.all
context = {'customers': customer}
return render(request, 'customer.html', context)
Now in your template you can get :
All product of a customer, with year by product
{% for customer in customers %}
Customer : {{ customer }}<br>
{% for product in customer.product_set.all %}
Product : {{ product }} / Year :{{ product.year }}<br>
{% endfor %}
{% endfor %}
But I don't like put too much logic in template.
I advise you to create the data of your table in your view, even better in your model. And then generate your html table with this data.
Summary of the function you will need:
You can replace all() by a filter()
products = customer.product_set.all() # give you all product of a customer
UPDATE AFTER COMMENTS:
Views.py
from django.shortcuts import render, get_object_or_404, redirect
from .models import Customer, Product, Year
# Create your views here.
def home(request):
customers = Customer.objects.all()
years = Year.objects.all().values_list('year', flat=True).asc() # List of year name
rows = []
for year in years:
row = [year] + [None] * len(customers) # Row with year in first column, and the rest init with same size of customers list
for idx, customer in enumerate(customers):
quantities = customer.product_set.filter(year__year=year).valu e_list('quantity', flat=True) # filter product by year. That can return multiple product !!!
row[idx + 1] = ' ,'.join(quantities) # create a string of quantities
rows.append(row) # Add new row in our rows list
context = {'customers': customer,
'rows': rows}
return render(request, 'customer.html', context)
template:
{% extends 'base.html' %}
{% block customer %}
<div class="container">
<h2>Players Table</h2>
<p>Customer with Product</p>
<table class="table">
<thead>
<tr>
<th>Year/Product</th>
{% for customer in customers %}
<th>{{ customer.name }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for row in rows %}
<tr>
{% for cell in row %}
<th>cell</th>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
I think that can help you to understand how to resolve your problem. I don't test this code, so maybe there is error. This is not perfect !

Related

Cannot see foreign key values in the template

This is my 2nd week learning Django. I'm trying to get comfortable with Django Template Language. I'm trying to make an Inventory app with 4 models. The views for them are class-based.
The templates for Ingredient and Menu work as expected. However, I'm struggling with trying to loop through values from the Purchase model which has a foreign key field 'menu_item'. The template is not showing anything from the for loop. I've referred numerous articles here to find most of them use function-based views. I've tried using {% for purchase in purchase_set %}, {% for purchase in purchase_set.all %}. I know the object to iterate over is a query-set. I cannot figure out what to do?
MODELS.PY
from django.db import models
# Create your models here.
class Ingredient(models.Model):
Pounds = 'lbs'
Ounces = 'oz'
Grams = 'gms'
Eggs = 'eggs'
Piece = 'piece'
Litre = 'litre'
unit_choices = [(Pounds, 'lbs'),
(Ounces, 'ounces'),
(Grams, 'grams'),
(Eggs, 'eggs'),
(Piece, 'piece'),
(Litre, 'litre')]
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=50)
unit_price = models.FloatField(default=0.0)
quantity = models.FloatField(default=0.0)
unit = models.CharField(max_length=10, choices=unit_choices)
class Meta:
ordering = ['id']
def __str__(self):
return self.name
class MenuItem(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=50)
price = models.FloatField(default=0.0)
class Meta:
ordering = ['id']
def __str__(self):
return self.title
class RecipeRequirement(models.Model):
menu_item = models.ForeignKey(MenuItem, on_delete=models.CASCADE)
ingredient = models.ForeignKey(Ingredient, on_delete=models.CASCADE)
quantity = models.FloatField(default=0.0)
def __str__(self):
return self.menu_item.title
class Purchase(models.Model):
menu_item = models.ForeignKey(MenuItem, on_delete=models.CASCADE)
timestamp = models.DateTimeField()
id = models.AutoField(primary_key=True)
class Meta:
ordering = ['id']
def __str__(self):
return self.menu_item.title
VIEWS.PY:
from django.shortcuts import render
from .models import Ingredient, MenuItem, RecipeRequirement, Purchase
from django.views.generic import ListView
def home(request):
return render(request, 'inventory/home.html')
class IngredientView(ListView):
model = Ingredient
template_name = 'inventory/ingredients.html'
class PurchaseView(ListView):
model = Purchase
template_name = 'inventory/purchases.html'
class MenuView(ListView):
model = MenuItem
template_name = 'inventory/menu.html'
PURCHASES.HTML
<h3>This is purchases page.</h3>
{% block content %}
<table>
{% for purchase in purchase_set.all %}
<tr>
<td>{{ purchase.title }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
The title field you are trying to show is in MenuItem model that is used as foreignkey in Purchase model with menu_item field. ListView class returns to context yours purchases in object_list key. So in Your purchases.html template:
<h3>This is purchases page.</h3>
{% block content %}
<table>
{% for purchase in object_list %}
<tr>
<td>{{ purchase.menu_item.title }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
PS. This `purchase_set' is referring to reverse relationship. So if you would want to show all purchases of MenuItem you would do:
{% for item in menuitems %}
<h1>{{ item }}</h1>
{% for purchase in item.purchase_set.all %}
<p>{{ purchase.timestamp }}</p>
{% endfor %}
{% endfor %}

Django initial loading of page taking too long

Hi I am a beginner at Django and I am working on a project that lists 100 companies in each page along with there contacts and also the amount of items sold. Here is an example:
As you can see the initial loading time of the page is very high. But when I refresh the page it refreshes very fast because I am using caching.
Here are some of my other files:
models.py
from __future__ import unicode_literals
from django.db import models
class Company(models.Model):
name = models.CharField(max_length=150)
bic = models.CharField(max_length=150, blank=True)
def get_order_count(self):
orders = self.orders.count()
return orders
def get_order_sum(self):
orders = Order.objects.filter(company=self)
total_sum = sum([x.total for x in orders])
return total_sum
class Meta:
ordering = ['-id']
class Contact(models.Model):
company = models.ForeignKey(
Company, related_name="contacts", on_delete=models.PROTECT)
first_name = models.CharField(max_length=150)
last_name = models.CharField(max_length=150, blank=True)
email = models.EmailField()
def get_order_count(self):
orders = self.orders.count()
return orders
class Order(models.Model):
order_number = models.CharField(max_length=150)
company = models.ForeignKey(Company, related_name="orders", on_delete=models.CASCADE)
contact = models.ForeignKey(Contact, related_name="orders", on_delete=models.SET_NULL, blank=True, null=True)
total = models.DecimalField(max_digits=18, decimal_places=9)
order_date = models.DateTimeField(null=True, blank=True)
added_date = models.DateTimeField(auto_now_add=True)
modified_date = models.DateTimeField(auto_now=True)
def __str__(self):
return "%s" % self.order_number
views.py
from django.shortcuts import render
# Create your views here.
from django.views.generic import ListView
from mailer.models import Company, Contact, Order
class IndexView(ListView):
template_name = "mailer/index.html"
model = Company
paginate_by = 100
The html
<div class="container">
<table class="table table-borderless">
{% if is_paginated %}
<tr><td>
{% if page_obj.has_previous %}
«
{% endif %}
</td>
<td></td>
<td></td>
<td>
{% if page_obj.has_next %}
»
{% endif %}
</td>
</tr>
{% endif %}
<tr>
<th>Name</th>
<th>Order Count</th>
<th>Order Sum</th>
<th>Select</th>
</tr>
{% for company in company_list %}
<tr>
<td>{{ company.name }}</td>
<td>{{ company.get_order_count }}</td>
<td>{{ company.get_order_sum|floatformat:2 }}</td>
<td><input type="checkbox" name="select{{company.pk}}" id=""></td>
</tr>
{% for contact in company.contacts.all %}
<tr>
<td> </td>
<td>{{ contact.first_name }} {{ contact.last_name }}</td>
<td>Orders: {{ contact.get_order_count }}</td>
<td></td>
</tr>
{% endfor %}
{% endfor %}
</table>
</div>
Is there any way in which I can reduce the initial load time. Please show me an efficient way to solve this problem.
Each {{company.get_order_count}} will hit the DB. Admittedly with a very simple query, but even so, it will slow things down.
You want to annotate the objects with this count. Use
from django.db.models import Count
class IndexView(ListView):
template_name = "mailer/index.html"
model = Company
paginate_by = 100
def get_queryset(self):
return super().get_queryset().annotate( num_orders=Count('orders') )
and replace {{ company.get_order_count }} with {{ company.num_orders }}. This will turn N+1 DB queries into one DB query.
That's the easy one. There's a similar problem with get_order_sum which can almost certainly be solved with another annotation involving the django.db.Sum. Sorry but its late and my stomach is growling and I don't have any confidence that I would get that one right straight off the top of my head.
The cheat sheet on annotation is here. You might also need to look at aggregation.
Oh, and install Django_debug-toolbar in your developer environment. Every time in future it gets slow, you can just click there to see what SQL was executed and how long it took.

How to display sum of disctinct items in django?

I would like to display sum all the distinct categories of the products that belongs to the user. I searched on the web, but all the things that I tried doensn't work. You may find the models,view and template below. It doesn't give me anything at the html.
Model:
class Product(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE,related_name='products')
category = models.CharField(max_length=120)
brand = models.CharField(max_length=120)
product = models.CharField(max_length=120)
price = models.DecimalField(decimal_places=2,max_digits=100)
class Comp_Product(models.Model):
product = models.ForeignKey(Product,on_delete=models.CASCADE, related_name="comp_products")
competitor = models.URLField()
price = models.DecimalField(decimal_places=2,max_digits=100)
change = models.FloatField()
stock = models.BooleanField()
last_update = models.DateField(auto_now_add=True)
View:
class DashboardList(ListView):
template_name='dashboard_by_user.html'
def get_queryset(self):
return Product.objects.filter(user=self.request.user).annotate(count_category=Count('category',distinct=True)).aggregate(sum_category=Sum('count_category'))
template:
{% for product in product_list %}
{{product.sum_category}}
{%endfor%}
welcome to stackoverflow,
I'm assuming that you want to count the number of distinct categories for the given user. This can be done like this:
views.py:
from django.views.generic.list import ListView
from .models import Product
class DashboardList(ListView):
template_name = 'dashboard_by_user.html'
def get_queryset(self):
return Product.objects.filter(user=self.request.user)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
Products = context['object_list']
context['distinct_category_count'] = Products.values(
'category').distinct().count()
return context
dashboard_by_user.html:
<h1>Products</h1>
<ul>
{% for product in object_list %}
<li>{{ product.brand }} - {{ product.product }} - {{ product.category }}</li>
{% empty %}
<li>No Producs yet.</li>
{% endfor %}
</ul>
<p> Number of distinct categories: {{ distinct_category_count }} </p>
This should give you an output similar to this one:

How to create a dynamic filter based on the primary key of ForeignKey in ListView?

I am relatively new to Django but the main problem I am facing right now is to create a ListView that will display uploaded documents based on the primary key of my ForeignKey.
I have tried several methods of trying to create the filter and read the online documentation on class-based view but it does not seem to have relevant information on how to use the primary key of my ForeignKey in my filter.
These are my models:
class Post(models.Model):
title = models.CharField(max_length=100)
image = models.ImageField(default = 'default0.jpg',
upload_to='course_image/')
description = models.TextField()
price = models.DecimalField(decimal_places=2, max_digits=6)
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
rating = models.IntegerField(default = 0)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk' : self.pk})
class Lesson(models.Model):
title = models.CharField(max_length=100)
file = models.FileField(upload_to="lesson/pdf")
date_posted = models.DateTimeField(default=timezone.now)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('lesson_upload', kwargs={'pk': self.pk})
Here is my ListView with the filter that is not working:
class LessonListView(ListView):
model = Lesson
template_name = 'store/uploaded_lesson.html'
context_object_name = 'lesson'
# def get_queryset(self):
# return Lesson.objects.filter(Post__pk=self.Post.pk)
def get_queryset(self):
self.post__pk = get_object_or_404(post__pk,
name=self.kwargs['post__pk'])
return Lesson.objects.filter(post__pk=self.post__pk)
Here is my urls.py:
path('post/<int:pk>/lesson_uploaded/', LessonListView.as_view(), name='lesson_uploaded'),
Here is my html:
{% extends "store/base.html" %}
{% block content %}
<div id="main">
<table class="table mb-0">
<thead>
<tr>
<th>Title</th>
<th>Author</th>
<th>Download</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{% for lesson in lesson %}
<tr>
<td>
{% if lesson.file %}
<img src="{{ lesson.file.url }}" style="width:100px;">
{% else %}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}
You can try like this:
In urls, add post_id :
path('lessons/<int:post_id>/', LessonListView.as_view()),
Then update the View to get the post_id in get_queryset method:
class LessonListView(ListView):
model = Lesson
template_name = 'store/uploaded_lesson.html'
context_object_name = 'lesson'
def get_queryset(self):
return Lesson.objects.filter(post_id=self.kwargs.get('post_id'))
Also, please don't name list and item of that list in a for loop same, so update it to:
{% for l in lesson %}. // or anything other than lesson
<tr>
<td>
{% if l.file %}

Displaying all of the fields of ManytoMany objects in Django Model Form

I am doing a simple invoice app and i encountered a problem with my django model form. Form is showing all of the ManytoManyfields objects but i would like to display all of their fields not just an objectname.
My models.py:
from django.db import models
import datetime
from django.urls import reverse
# Create your models here.
class Product(models.Model):
item_name = models.CharField(max_length=100)
chinese_name = models.CharField(max_length=100)
erp_number = models.CharField(max_length=20)
unit = models.CharField(max_length=8)
photo = models.ImageField(blank=True, null=True)
price_usd = models.DecimalField(max_digits=8, decimal_places=2)
price_eur = models.DecimalField(max_digits=8, decimal_places=2)
def __str__(self):
return self.item_name
class Batch(models.Model):
batch_name = models.CharField(max_length=100)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.DecimalField(max_digits=5, decimal_places=0)
def __str__(self):
return self.batch_name
class Invoice(models.Model):
invoice_no = models.CharField(max_length=30, default="Proforma Invoice 2018102300001")
products = models.ManyToManyField(Product)
total = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
date = models.DateField(auto_now_add=False,auto_now=False, default=datetime.datetime.now())
def __str__(self):
return self.invoice_no
def get_absolute_url(self):
return reverse('invoice_detail',kwargs = {'pk':self.pk})
My forms.py:
from django import forms
from django.forms import modelformset_factory, inlineformset_factory
from .models import Invoice, Product
from django.forms.widgets import CheckboxSelectMultiple
class InvoiceForm(forms.ModelForm):
invoice_no =forms.CharField(widget=forms.TextInput(attrs={'size':40}),initial='Proforma Invoice 2018102300001')
date = forms.DateField(widget = forms.SelectDateWidget)
class Meta:
model = Invoice
fields = '__all__'
def __init__(self, *args, **kwargs):
super(InvoiceForm, self).__init__(*args, **kwargs)
self.fields["products"].widget = CheckboxSelectMultiple()
self.fields["products"].queryset = Product.objects.all()
My template:
{% extends 'base.html' %}
{% block content %}
<!DOCTYPE html>
<html lang="en">
<body>
<form method="POST" action="">
{% csrf_token %}
<div clas="row">
<p style="width:20px">{{ form.invoice_no }}</p>
<p>{{ form.date }}</p>
</div>
<div class ="row">
<div class="col-md-12">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th scope="col" >Photo</th>
<th scope="col">Name</th>
<th scope="col">ERP number</th>
<th scope="col">Unit</th>
<th scope="col">Price (USD)</th>
<th scope="col">Price (EUR)</th>
</tr>
</thead>
<tbody>
{% for product in form.products.all %}
<tr>
<th scope="row"></th>
<td>{{product.item_name}}</td>
<td>{{form.products.erp_number}}</td>
<td>product.unit</td>
<td>product.price_usd</td>
<td>product.price_eur</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<input type="submit" value="Submit">
</form>
</body>
</html>
{% endblock %}
As you can see i am trying to access the fields of Product model by using {% for product in form.products.all %} by it is not working. I've been looking for solution on stackoverflow but haven't found the right answer. Is the a way to display not just an objectname and still keep the self.fields["products"].widget = CheckboxSelectMultiple() widget ?