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.
Related
I am trying to show number of articles in each category in my django project. But it shows category id instead of category_name. I want to display category_name and the corresponding number of articles.
blog/views.py
def searchView(request):
statistics = Post.objects.values('cid').annotate(num_articles = Count('cid')).order_by()
return render(request, 'blog/search.html', {'statistics': statistics})
blog/search.html -> here stat.cid shows the category id but I want to show category_name here.
{% extends 'users/base.html' %}
{% block content %}
<div class="container">
<br>
<div class="row text-center">
<div class="col-md-3"> </div>
<div class="col-md-6">
<h4 class="p-2 mb-2 bg-secondary text-white">POPULAR CATEGORIES!!</h4>
<table id="myTable" class="table table-bordered table-hover table-striped shadow-sm bg-white rounded">
<thead>
<tr>
<th>Category</th>
<th>Articles Available</th>
</tr>
</thead>
<tbody>
{% for stat in statistics %}
<tr>
<td>
{{ stat.cid }}
</td>
<td>
{{ stat.num_articles }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock content %}
blog/models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth import get_user_model
from django.urls import reverse
from ckeditor.fields import RichTextField
# Create your models here.
class Category(models.Model):
cid = models.AutoField(primary_key=True)
category_name = models.CharField(max_length=100)
def __str__(self):
return self.category_name
class Post(models.Model):
aid = models.AutoField(primary_key=True)
image = models.ImageField(default='blog-default.png', upload_to='images/')
title = models.CharField(max_length=200)
content = RichTextField()
created = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
cid = models.ForeignKey(Category, on_delete=models.CASCADE, verbose_name='specialization')
approved = models.BooleanField('Approved', default=False)
like = models.ManyToManyField(get_user_model(), related_name='likes', blank=True)
def __str__(self):
return self.title
Post.objects.values('cid') would only give you the pk of the Category. To access the category_name of the Category you should also include that in the values():
# Note the double underscore between cid and category_name
`Post.objects.values('cid', 'cid__category_name')`
Now that the category_name is available, access it in the template like this:
{{ stat.cid__category_name }}
While this is specific to your case, a better answer is here:
https://stackoverflow.com/a/27181936/10951070
I would be going at this from the opposite direction, meaning I would be accessing this data from Category rather than from Post which for some reason you call statistics.
First off I'd suggest you to use a ListView and then I'd proceed as follows:
# views
from django.views.generic import ListView
class CategoryListView(ListView):
model = Category
template_name = 'blog/search.html'
context_object_name = "categories"
# template
<table>
<thead>
...
</thead>
<tbody>
{% for category in categories %}
<tr>
<td>{{ category.cid }}</td>
<td>{{ category.post_set.count }}</td>
</tr>
{% endfor %}
</tbody>
</table>
If you add a related_name in your Post model in the cid attribute and call it posts, you can then in the template write category.posts.count which is more readable. This is just a personal preference and definitely not a rule.
I want to take two fields from my model, one is a float, and the other is a ManyToMany and do an arithmetic operation on them and add them in a field with annotate
At one point it worked for me and then it didn't.
I don't know what would be the correct way I show some things from my files.
models.py
from django.db.models import F, DecimalField
class Tax(models.Model):
name = models.CharField(max_length=50)
porcent = models.IntegerField()
def __str__(self):
return self.name
class Product(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
code_number = models.IntegerField(verbose_name='Bar Code')
image = models.ImageField(upload_to='products/')
purchase_price = models.DecimalField(max_digits=10, decimal_places=2)
sale_price= models.DecimalField(max_digits=10, decimal_places=2)
tax = models.ManyToManyField(Tax, blank=True, related_name="tax")
description = models.TextField()
created_date = models.DateTimeField(
default=timezone.now)
published_date = models.DateTimeField(
blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
views.py
from django.shortcuts import render
from .models import Tax, Product, Invoice
from django.db.models import F, DecimalField
class InvoiceDashboard(View):
def get(self, request, *args, **kwargs):
products=Product.objects.all().annotate(amount_tax = ((F('sale_price') / 100) * F('tax__porcent'))).annotate(
price_total=(F('sale_price') + F('amount_tax'))
)
context = {
'products': products,
}
return render(request, 'pos/cashier.html', context)
pos/cashier.html
{% extends 'pos/base.html' %}
{% block content %}
<tbody id="table_p">
{% for product in products %}
<tr class="productO" id="{{ product.id }}" data-id="{{ product.id }}" data-saleprice="{{ product.sale_price }}" data-codenumber="{{ product.code_number }}" data-amounttax="{{ product.amount_tax|floatformat:2 }}">
<th scope="row">{{ product.code_number }}</th>
<td><img src="/media/{{ product.image }}" width="60" height="60"/>{{ product.title }}</td>
<td><input class="spinner" id="{{ product.id }}" type="number" value="1" placeholder="1" min="1" max="100" disabled></td>
<td class="sub-total-p" id="{{ product.sale_price }}">{{ product.sale_price }}</td>
<td>{% for tax in product.tax.all %}{{ tax }} {% endfor %} | {{ product.amount_tax|floatformat:2 }}</td>
<td class="total-p" id="{{ product.price_total }}">{{ product.price_total|floatformat:2 }}</td>
<td class="sub-select">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="{{ product.id }}">
</div>
</td>
</tr>
{% endfor %}
</tbody>
{% endblock %}
How can I get the percentage of each of the Taxes in a Product Object?
I would wager a wild guess and say your Tax objects have interger fields without a value.
You haven't specified a default.
Secondly if its not what I said above, because it's a ManyToMany relationship, it doesn't necessarily mean you created any tax objects.
E.g. simply by creating a Product, doesn't create any related Tax objects.
Therefore trying to aggregate or annotate an integer across the ManyToMany will result in a NULL value.
I think the problem with your code above is that in your annotation using tax__porcent, you are not reflecting the fact that their are (potentially) multiple porcent values because it's a many to many relationship between product and tax. So a product won't have single tax_amount, but one for every tax porcent value. Unfortunately, annotations tend to label the calling model, not the related models.
There's two methods I can think of that would handle this. One is to use a through table (docs) for your manytomany. That will enable you to grab one record for each product/tax combination that you can then annotate to your hearts content.
The other method, whch is probably simpler if you want to keep your existing db structure, is to split the annotation process out of the queryset. You can add fields to querysets before sending them to your template, so something like the following might work, as long as you don't requery later:
views.py
products=Product.objects.all().prefetch_related('tax')
for product in products:
for tax in product.tax.all():
tax.amount_tax = (product.sale_price / 100) * tax.porcent
tax.price_total = product.sale_price + tax.amount_tax
template.html
<td>
{% for tax in product.tax.all %}
{{tax}}{{ tax.amount_tax | floatformat:2}}
{% endfor %}
</td>
I have the following models
AcsObject class
class AcsObjects(models.Model):
object_id = models.IntegerField(primary_key=True)
object_type = models.ForeignKey('AcsObjectTypes', db_column='object_type')
context = models.ForeignKey('self', blank=True, null=True)
security_inherit_p = models.BooleanField()
creation_user = models.IntegerField(blank=True, null=True)
Projects class
class ImProjects(models.Model):
project = models.ForeignKey('AcsObjects',related_name='project', on_delete=False, primary_key=True)
project_name = models.CharField(max_length=1000)
project_nr = models.CharField(max_length=100)
project_path = models.CharField(max_length=100)
TimesheetTasks class
class TimesheetTasks(models.Model):
task = models.ForeignKey('Projects', related_name='t_task', on_delete=False, primary_key=True)
uom = models.ForeignKey('Categories', related_name='u_uom', on_delete=False)
planned_units = models.FloatField(blank=True, null=True)
billable_units = models.FloatField(blank=True, null=True)
I wrote the following code into views.py file.
class TimesheetData(TemplateView):
template_name = 'timesheet.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["da"] = TimesheetTasks.objects.all()
return context
I want to print a project_name but it is giving me a task_id ( task_id and project_id are same) using jinja template.
timesheet.html
<body>
<p> {{da}} </p>
<table class="table table-light">
<thead class="thead-light">
<tr>
<th>Task </th>
</tr>
</thead>
<tbody>
{% for timesheet in da %}
<tr>
<td> {{timesheet.task}} </td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
It is giving me a blank output
Output
That's simply just because you're not access to product_name field yet.
<tbody>
{% for timesheet in da %}
<tr>
<td> {{timesheet.task}} </td>
</tr>
{% endfor %}
</tbody>
With this, Jinja just render the task id (project id), because Jinja think that you're not need any other information but just the foreign key value. So to be able to see the project_name, you should use this instead: timesheet.task.project_name, it does the job.
<tbody>
{% for timesheet in da %}
<tr>
<td> {{timesheet.task.project_name}} </td>
</tr>
{% endfor %}
</tbody>
Just want to mention that this could lead to another issue (performance issue) when Jinja have to query the data when render the template. To get rid of that, consider to use select_related which is an API of Django Queryset, check it out and gain some experiment that API, it really useful when using Django.
I have a page that displays a snapshot of projects in a list of boxes, each box has project owner, title date, that kind of thing, there is also a window within that separates the updates into 7 categories, the updates are in a separate table. Looping through the projects is fine, but the updates come from all the projects. How do I filter out the other projects?
I can post my code, but I have done this 2 or 3 times in the past month with no response so suspecting that my structure is completely wrong.
To give you an example of what I am after, to be able to pull out the latest update that is filtered for Project id, and then update category. Is this possible?
EDIT: adding template and css so you can hopefully see graphically what it is I am trying to do.
models.py:
from datetime import datetime
from django.conf import settings
from django.contrib.auth.models import Permission, User
from django.db import models
from django.urls import reverse
User = settings.AUTH_USER_MODEL
class Area(models.Model):
'''
Project area - Digital, Uplink, DTH VoD, Interactive, OTT Streaming,
OTT VoD, OTT Platform, Operational Improvement, Archive.
'''
name = models.CharField(max_length=45, unique=True)
code = models.CharField(max_length=45, unique=True)
def __str__(self):
return self.name
class Priority(models.Model):
'''
Project priority - Low, Medium, High - defualt set to Low
'''
priority = models.CharField(max_length=16, unique=True)
colour = models.CharField(max_length=7, unique=True)
def __str__(self):
return self.priority
class Project(models.Model):
'''
Main Project, serves the default Projects Portal window.
'''
date_published = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
area = models.ForeignKey(Area, on_delete=models.PROTECT)
title = models.CharField(max_length=128, unique=True)
summary = models.CharField(max_length=256)
others = models.CharField(max_length=128, blank=True)
deadline = models.DateField(null=True, blank=True)
priority = models.ForeignKey(Priority, on_delete=models.PROTECT)
closed = models.DateTimeField(null=True, blank=True)
def __str__(self):
return self.title
class UpdateCategory(models.Model):
'''
Updates are split into 6 categories that applies to each project
with 2 extra categories of Other Updates and Mitigating issues.
'''
name = models.CharField(max_length=24, unique=True)
def __str__(self):
return self.name
class Update(models.Model):
'''
Latest update must have a category from UpdateCategory
and be linked to a project, all updates are timestamped.
'''
project = models.ForeignKey(Project, on_delete=models.CASCADE)
category = models.ForeignKey(UpdateCategory, on_delete=models.PROTECT)
update = models.TextField(max_length=2048, blank=True)
added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.update
So each project has it's updates (update to the state of the project) split into 7 different categories as each project follows the same structure, the last category "other updates" being an catchall for issues/blockers to the project, being able to see the last update from each project in one small block gives a nice overview of where the project is at a quick glance. Visually, each project will be shown as a block showing the last update for each category. I am thinking that I need to set up a function that picks the latest update from each category (a blank or null value is completely valid and should just be blank or white space), but I can't work out how this will be filtered for each project as the looping happens within the template at the moment.
{% extends "project_portal/base.html" %}
{% block home %}
<div id="main">
<div id="content">
<hgroup>
<h1>OTT</h1>
<h2>Service Integration Development Project Portal</h2>
</hgroup
</div>
{% if object_list %}
{% for project in project_list %}
<div class="container">
<div class="box1">
<h4>{{ project.title }}</h4>
<p>Project Status</p>
<div class="square"></div>
</div>
<div class="box2">
<strong>{{ project.summary }}</strong>
</div>
<div class="box3">
<strong>{{ project.user }}</strong>
</div>
<div class="box4">
<table class="items">
<col width="160px">
<tr class="updates">
<!-- <td>Monitoring Status</td> -->
<td>Impact Scenario</td>
<td>✅</td>
</tr>
<tr class="updates">
<td>Support Model</td>
<td>✅</td>
</tr>
<tr class="updates">
<td>Monitoring Status</td>
<td>✅</td>
</tr>
<tr class="updates">
<td>Training</td>
<td>✅</td>
</tr>
<tr class="updates">
<td>Service Rehearsals</td>
<td></td>
</tr>
<tr class="updates">
<td>Documentation</td>
<td></td>
</tr>
<tr class="updates">
<td>Other Updates</td>
<td></td>
</tr>
</table>
</div>
<div class="box5">
{% for item in update_category %}
<table>
<tr>
<td>{{ update.id }}</td>
</tr>
</table>
{% endfor %}
</div>
<div class="box6">
<h4>Project Timeline</h4>
</div>
<div class="box7">TimeLine</div>
</div>
{% endfor %}
{% endif %}
</div>
{% endblock %}
So I am undertaking a website project and I am having some issues with one page.
I essentially load NBA teams wins and losses onto a page and make my own makeshift standings ladder.
The data loaded from my model is as follows:
MODEL.PY
from django.db import models
from django.utils import timezone
# Create your models here.
class Team(models.Model):
team_name = models.CharField(max_length=500, unique=True, primary_key=True)
wins = models.PositiveIntegerField()
losses = models.PositiveIntegerField()
created_date = models.DateTimeField(default=timezone.now)
I use the ListView in my views.py file as follows:
VIEWS.PY
class LadderListView(LoginRequiredMixin, ListView):
#this defaults to looking for the models name html so in this case team_list.html
# so i changed it to template_name variable
template_name = 'merchbet/ladder_list.html'
model = models.Team
and render the urls.py file as follows:
path('ladder/', views.LadderListView.as_view(), name='ladder_list'),
all of this successfully works.
When I get to my html code I am able to the teams, and their win/loss records to print to the website with template tagging, but I am looking to inject one line at the bottom which shows the "Last Updated: {{ team.created_date }}.
This is not working and I am not getting an errors, the "{{ team.created_date }}" just shows nothing on the url page.
I have looked through the docs and can not see what I am doing wrong.
This is my html code:
LADDER_LIST.HTML
{% extends 'merchbet/base.html' %}
{% block content %}
<div class="container">
<table>
<tbody>
<tr>
<td>Team</td>
<td>Wins</td>
<td>Losses</td>
</tr>
{% for teams in object_list %}
<tr>
<td>{{ teams.team_name }}</td>
<td>{{ teams.wins }}</td>
<td>{{ teams.losses }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<h2 style="color:white;"> testing {{ team.created_date }}</h2>
{% endblock %}
Thanks for any help in advance.
You don't have anything called team in your template.
If you just want to display the value from a single team in the lst, and don't care which one, just grab the first or last:
{{ object_list.0.created_date }}
you just need to edit your models and that should fix your problem
class Team(models.Model):
....
created_date = models.DateTimeField(auto_now=False, auto_now_add=True) #when you first add team
updated = models.DateTimeField(auto_now=True, auto_now_add=False) #when you update
class Meta :
ordering = ["-updated","created_date"]