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>
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 have models which is Player and Position. Player is associated with position player is currently at. You can assign a player new position, which is why I need it to use it in some form. But in that form I see that Position is position Object (see image bellow), but I would like to put "Position.name" in there to see the name of choices instead that object, but I have no Idea how to do it.
Thank you for your help.
models.py
class Position(models.Model):
name = models.CharField(max_length=20)
short = models.CharField(max_length=2)
class Players(models.Model):
name = models.CharField(max_length=200)
positionId = models.ForeignKey(Position, on_delete=models.CASCADE, null=True, blank=True)
forms.py
class CompositionForm(ModelForm):
class Meta:
model = Players
fields = ("positionId", ... many more ... )
table.html
<table id="nominated-player-table" class="table table-bordered" style="">
{% for form in formset.forms %}
{% if forloop.first %}
<thead>
<td colspan="15"
style="background-color: dimgray; color: white; border-top-right-radius: 15px; border-top-left-radius: 15px; border: none">
Nominovaní hráči
</td>
<tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr>
</thead>
{% endif %}
<tr>
{% for field in form.visible_fields %}
<td>
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
You need add to your models the magic methods __str__ or __unicode__ , because Django itself don't know how to convert model instances to string, and represents it as ModelName object
# In python 2.x you can uncoment next line.
# from __future__ import unicode_literals
class Position(models.Model):
name = models.CharField(max_length=20)
short = models.CharField(max_length=2)
def __unicode__(self):
return self.name
class Players(models.Model):
name = models.CharField(max_length=200)
positionId = models.ForeignKey(
Position, on_delete=models.CASCADE, null=True, blank=True)
def __unicode__(self):
return self.name
If you need more pesonalization, you can use label_from_instance in the form:
class CustomModelChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
return 'The position with name %s' % obj.name
class CompositionForm(forms.ModelForm):
positionId = CustomModelChoiceField(queryset=Position.objects)
class Meta:
model = Players
fields = ("positionId", ... many more ... )
I ran into this the other day, but with a ModelChoiceField in an ordinary form, not a ModelForm. You subclass ModelChoiceField to override the choices labels generation. From the Django docs:
from django.forms import ModelChoiceField
class MyModelChoiceField(ModelChoiceField):
def label_from_instance(self, obj):
return "My Object #%i" % obj.id
In your case include obj.PositionId.name in the label
Something similar should be possible for ModelForms. Check docs. Yes, modelform_factory has a field_classes argument so you can override the default ModelChoiceField
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.
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"]
I'm quite new with python and django and I apologize if the topic was already covered, but I coudln't find an answer to my question.
I have theese classes in my models.py
class Category(models.Model):
category_type = models.CharField(max_length=50)
description = models.TextField()
def __unicode__(self):
return self.category_type
class Area(models.Model):
area_type = models.CharField(max_length=50)
description = models.TextField()
def __unicode__(self):
return self.area_type
class Topic(models.Model):
topic_type = models.CharField(max_length=50)
description = models.TextField()
def __unicode__(self):
return self.topic_type
class Tag(models.Model):
tag_type = models.CharField(max_length=50)
description = models.TextField()
def __unicode__(self):
return self.tag_type
class GenericRecord(models.Model):
document_title = models.CharField(max_length=500)
category = models.ForeignKey("Category")
area = models.ForeignKey("Area")
topic = models.ForeignKey("Topic")
tag = models.ForeignKey("Tag")
note = models.TextField(null=True, blank=True)
link = models.TextField(null=True, blank=True)
file_upload = models.FileField(upload_to='GenericRecord/', null=True, blank=True)
class Meta:
abstract = True
class Document(GenericRecord):
code = models.CharField(max_length=500, null=True, blank=True)
reference = models.CharField(max_length=500)
issue_date = models.DateField(auto_now=False, auto_now_add=False)
validation_date = models.DateField(auto_now=False, auto_now_add=False, null=True, blank=True)
def get_admin_url(self):
return reverse("admin:%s_%s_change" % (self._meta.app_label, self._meta.model_name), args=(self.id,))
def __unicode__(self):
if self.code:
return "%s-%s" % (self.code, self.document_title)
else:
return "--%s" % self.document_title
And this piece of code in views.py
def documents_list(request):
# Generate counts of some of the main objects
num_docs=Document.objects.all().count()
docs=Document.objects.all()
num_articles=Article.objects.all().count()
articles=Article.objects.all()
template='documents_management.html'
for object in docs:
object.fields = dict((field.name, field.value_to_string(object)) for field in object._meta.fields)
# Render the HTML template documents_management.html with the data in the context variable
return render(request,template, context={'docs':docs, 'num_docs':num_docs,'docs':docs, 'num_articles':num_articles, 'articles':articles})
In the template I'm trying to get a table with all the values, but for the related objects I get the primary key (of course).
Here is the code in my template:
<table class="w3-table-all">
{% for object in docs %}
{%if forloop.first %}
<tr>
{% for field, value in object.fields.iteritems %}
<th>{{ field }}</th>
{% endfor %}
</tr>
{%endif%}
{% endfor %}
{% for object in docs %}
{% for field, value in object.fields.iteritems %}
<td>{{ value }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
What I see in my browser
My question is, how can I get the object Category, Area etc... in order to get the category_type, area_type etc. value?
Thanks!
Here is an great example from the docs: https://docs.djangoproject.com/en/1.11/intro/tutorial03/#use-the-template-system
What you are searching for is the question.choice_set.all part.
UPDATE due to a hint of bad style
As mentioned by daniel you should ditch the Field.to_value_string method.
Since I am not a fan of implicit code I always recommend to code templates as explicit as possible, here would by my version of your template
<table class="w3-table-all">
{% for doc in docs %}
{% if forloop.first %}
<tr>
<th>Document Title</th>
<th>Category Type</th>
<th>Area Type</th>
<th>...</th>
</tr>
{% else %}
<tr>
<td>{{ doc.document_title }}</td>
<td>{{ doc.category.category_type }}</td>
<td>{{ doc.area.area_type }}</td>
<td>...</td>
</tr>
{% endif %}
{% endfor %}
</table>
What I changed:
only one for loop, you started with the if forloop.first you might also finish with the else case
refactored object to doc because objects is used often within django for model managers
add the fields explicit doc.area.area_type, this will prevent a new field in the model to also appear in the template but here I recommend an explicit over an implicit coding style
Also you can remove this from document_list:
for object in docs:
object.fields = dict((field.name, field.value_to_string(object)) for field in object._meta.fields)
The problem is in your use of Field.value_to_string. As the docstring on that method shows, this is for serialization, not for displaying values.
A much simpler and more effective way of doing this would be to use getattr, which gets the actual value; the template will then take care of converting those to a string, which in the case of the foreign keys will call the __unicode__ method of the related objects.
object.fields = dict((field.name, getattr(obj, field.name)) for field in object._meta.fields)