I try to calculate the average amount for each month for the 13 last month.
So I have a card with the date which has many amounts which are linked to a category.
For example my card 1 has an amount for category A, an amount for category B an amount for category C .... Amount, Card and Category have their own class in the model.
My objective is to calculate for one category, the average amount for each 13 last month.
Here is my model:
class Card(models.Model):
date = models.DateField(auto_now_add=False, null=False)
day = models.IntegerField(null=False)
comment = models.TextField(null=False)
worked = models.BooleanField(default=True)
def __str__(self):
return "<id={}, date={}, day={}, comment={}, worked={}>".format(
self.id,
self.date,
self.day,
self.comment,
self.worked
)
class Category(models.Model):
name = models.CharField(max_length=100)
icon = models.CharField(max_length=50)
order = models.IntegerField(null=False, unique=True)
image = models.CharField(max_length=255)
def __str__(self):
return "<id={}, name={}, icon={}>".format(self.id, self.name, self.icon)
class Amount(models.Model):
amount = models.DecimalField(max_digits=10, decimal_places=2)
card = models.ForeignKey(Card, on_delete=models.CASCADE, related_name='amounts')
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='amounts')
def __str__(self):
return "<id={}, amount={}>".format(self.id, self.amount)
And I have really no idea of how to do this.
Thanks for your help
The solution below uses Django's aggregation and Transform to give you for one category called category the average of the amounts in each month in range(13). If you need it to just be the previous 13 months I would suggest using datetime.timedelta and include a filter on card__date__year. You could also combine the filters below into one line but it gets a little long...
from django.db.models import Avg
amounts_in_category = Amount.objects.filter(category = category)
for month in range(13):
amounts_in_month = amounts_in_category.filter(card__date__month = month)
average_amount_for_month = amounts_in_month.aggregate(Avg('amount'))
Related
I am trying to print a particular wise total in the invoice using Django. The problem is if I use for loop, it prints the last items particular wise total in everywhere. I am confused what will be the best way to calculate individual total in their respective rows?
Here is my Views.py:
def order_complete(request):
order_number = request.GET.get('order_number')
transID = request.GET.get('payment_id')
try:
order = Order.objects.get(order_number=order_number, is_ordered=True)
ordered_products = OrderProduct.objects.filter(order_id = order.id)
total=0
subtotal = 0
for i in ordered_products:
total = i.product_price * i.quantity
subtotal += total
payment = Payment.objects.get(payment_id = transID)
context ={
'order': order,
'ordered_products': ordered_products,
'order_number': order.order_number,
'transID': payment.payment_id,
'payment': payment,
'subtotal': subtotal,
'total':total,
}
return render(request, 'orders/order_complete.html', context)
except(Payment.DoesNotExist, Order.DoesNotExist):
return redirect('home')
models.py
class OrderProduct(models.Model):
order = models.ForeignKey(Order, on_delete=models.CASCADE)
payment = models.ForeignKey(Payment, on_delete=models.SET_NULL, blank=True, null=True)
user = models.ForeignKey(Account, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
variations = models.ManyToManyField(Variation, blank=True)
quantity = models.IntegerField()
product_price = models.FloatField()
ordered = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.product.product_name
template
<dd class="text-right">${{ total }}</dd>
suggestion/solutions would be highly appreciated.
Here is the result
My suggestion is to always store total_amount in the table itself along with unit_price and quantity,
Cause this way, if you ever want to get the item's unit cost you can simply access it on order table itself and total to tell that this is the total of unit price x quantity
1 more advantage you get by this i.e. when calculating the final total of all order items you can simply write the following to get it
from django.db.models import Sum
order_products = OrderProduct.objects.filter(order_id=order.id)
total_amount = order_products.aggregate(Sum('total_amount'))["total_amount__sum"]
# total_amount is the new field to add in OrderProduct table which stores
# quantity * unit_price at the time of creation
This way, you will directly get individual total & sum of those totals
I want to develop DJANGO Application for rooms booking.
I want to use following TWO models.
class Room(models.Model):
room_no = models.IntegerField()
remarks = models.CharField(max_length=100)
def __str__(self):
return self.remarks
class Roombooking(models.Model):
room = models.ForeignKey(Room, related_name= 'roombookingforroom', on_delete=models.CASCADE)
booked_for_date = models.DateField(blank=True, null=True)
booked_by = models.TextField(max_length=1000, default='')
remarks = models.CharField(max_length=100,)
class Meta:
constraints = [
models.UniqueConstraint(
fields=["suit", "booked_for_date"],
name="unique_room_date",
),
]
def __str__(self):
return self.room.remarks
To avoid assigning one room to 2 different persons on any day, “ UniqueConstraint” is used.
Now, how to query the list of rooms which are vacant from DATE1 to DATE2
You can just filter the room booking by date
gte = greater than or equal to
lte = lower than or equal to
query = Roombooking.objects.filter(booked_for_date__gte=DATE1, booked_for_date__lte=DATE2)
*Note that DATE1 and DATE2 should be datetime type
You can have a look on official documentation
Models.py
class Entity(models.Model):
entity = models.CharField(max_length=40)
class Period(models.Model):
period = models.CharField(max_length=10)
class Product(models.Model):
entity = models.ForeignKey(Entity, on_delete=models.CASCADE, default=None, blank=True, null=True)
period = models.ForeignKey(Period, on_delete=models.CASCADE, default=None, blank=True, null=True)
sku = models.CharField(max_length=12)
class Sale(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE, default=None, blank=True, null=True)
price = models.DecimalField(max_digits=11, decimal_places=2)
Views.py
if request.method == 'POST':
if form.is_valid():
entityquery = form.cleaned_data['entity']
periodquery = form.cleaned_data['period']
entities = Entity.objects.get(entity=entityquery)
periods = Period.objects.get(period=periodquery)
products = Product.objects.filter(entity=entityquery, period=periodquery).values('id', 'period', 'entity', 'sku')
for sales in products.iterator():
sales = Sale.objects.filter(product__sku=product.sku, product__entity=entityquery, product__period=periodquery).aggregate(Sum('price'))
return sales
args = {'form': form, 'products': products, 'periods': periods, 'entities': entities, 'sales': sales}
return render(request, "products_list.html", args)
Expected Result
So far I am able to list all the SKU items that were sold based on the criteria (Period and Entity). Lets assume SKU 12 has two sales $10 and $30 and SKU 23 has three sales $5, $5 and $6
and I need to show the total sales for each of those products.
Input
Entity: Company XZY
Period: November
Output
SKU Total Sales
12 40.00
23 16.00
It is possible to do group by and SUM using the the django ORM.
How to query as GROUP BY in django?
from django.db.models import Sum
sales = Sale.objects.filter(product__sku=product.sku, product__entity=entityquery, product__period=periodquery).values(`product`).annotate(total = Sum('price'))
Also, in most cases, is very inefficient to loop over a query.
If at all possible, try to use the built-in ORM methods to avoid this.
This is known as a N+1 query problem.
Please note: Similar questions didn't help me as they have the category-foreignkey in the same class.
I have a simple Invoice app with models Invoice, Position, Product and Category. The Product is bound to the Category.
My target is to create a queryset that
filters e. g. a specific date-range
and then group all categories and build their sums
Here is a screenshot of the invoice respectively of its positions:
The expected result of the grouped query should look like this:
Can you help me to create a query that groups and sums the categories within the filtered date-range?
The only solution I was able to create was the filter of a specific date-range:
queryset = Position.objects.filter(invoice__date_of_purchase__range=['2019-01-01', '2019-12-31'])
models.py (which I have simplified):
from django.db import models
from django.urls import reverse
class Category(models.Model):
name = models.CharField(max_length=30, unique=True)
def __str__(self):
return self.name
class Product(models.Model):
name = models.CharField(max_length=120)
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='products')
def __str__(self):
return self.name
class Invoice(models.Model):
invoice_code = models.CharField(max_length=15)
date_of_purchase = models.DateField()
customer_name = models.CharField(max_length=100)
def __str__(self):
return self.invoice_code
class Position(models.Model):
invoice = models.ForeignKey(Invoice, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.DecimalField(decimal_places=2, max_digits=6)
price = models.DecimalField(decimal_places=2, max_digits=8)
total = models.DecimalField(
decimal_places=2, max_digits=8, blank=True, null=True) # is calculated in view
def __str__(self):
return self.product.name
The following filter will return all categories that have an invoice in the date range and will also filter the annotation to sum only those positions for those invoices
categories = Category.objects.filter(
products__position__invoice__date_of_purchase__range=['2019-11-17', '2019-12-31']
).annotate(
sum=Sum('products__position__total')
)
Each category will now be annotated with an attribute "sum"
for category in categories:
print(category, category.sum)
I'm going to suggest a tweak based on my experience.
Put position into the invoice model as a many to many fields. This should make it cheaper to filter the date range of invoices. It also may help to add a "sent" bol field depending on your use case.
Either in your view or in a utils.py file. Loop thru the query set's "Position's" field with the category as the conditional to separate by category and += the Position.total field to your awaiting variable.
absolute n00b here, just fiddling around. Trying to make a very simple app to track my personal expenses. I have a class for entering the expenses, a class for the categories and a class for my account balance. Plan is to create en entry in the account balance everytime I create an expense.
How do I update my account balance? I'll have to get fields from the latest entry in expenses to do the math with in my balance class, right?
This is what I have. Any help appreciated.
from django.db import models
from django.utils import timezone
class Category(models.Model):
category = models.CharField(max_length=200,blank=False)
def __str__(self):
return self.category
class Balance(models.Model):
date = models.DateTimeField(default=timezone.now)
previous_balance = ????
transaction = ????
current_balance = previous_balance - transaction
class Expense(models.Model):
date = models.DateTimeField(default=timezone.now)
spender = models.ForeignKey('auth.User')
description = models.CharField(max_length=200)
category = models.ForeignKey(Category,default=1)
ABN = 'ABN'
ING = 'ING'
ACCOUNT_CHOICES = (
(ABN, 'ABN'),
(ING, 'ING'),
)
account = models.CharField(
max_length=30,
choices=ACCOUNT_CHOICES,
default=ABN,
)
amount = models.DecimalField(max_digits=10, decimal_places=2)
def commit(self):
self.commit_date = timezone.now()
self.save()
def __str__(self):
return u"%s. Kosten: %s" % (self.description, self.amount)
If I'm understanding your question correctly, you want to be able to get your current balance after creating Expenses. If so, you can use Django's aggregation:
from django.db.models import Sum
class Balance(models.Model):
date = models.DateTimeField(default=timezone.now)
# Keep the amount you start with
starting_balance = models.IntegerField()
# Get the Sum of all expenses and do some simple subtraction
def get_current_balance(self):
total_expenses = Expense.objects.all().aggregate(Sum('amount'))
return self.starting_balance - total_expenses['amount__sum']
Then in your views, you can do something like:
current_balance = some_balance_instance.get_current_balance()
considered balance change will be trigger by expense record change, you can overwrite save on Expense model. then balance table can be maintain in auto.
import datetime
class Expense(models.Model):
date = models.DateTimeField(default=timezone.now)
spender = models.ForeignKey('auth.User')
description = models.CharField(max_length=200)
category = models.ForeignKey(Category,default=1)
ABN = 'ABN'
ING = 'ING'
ACCOUNT_CHOICES = (
(ABN, 'ABN'),
(ING, 'ING'),
)
account = models.CharField(
max_length=30,
choices=ACCOUNT_CHOICES,
default=ABN,
)
amount = models.DecimalField(max_digits=10, decimal_places=2)
def save(self, *args, **kwargs):
super(Expense, self).save(*args, **kwargs)
last_bal = Balance.objects.order_by('id').last()
Balance.objects.create(date=datetime.datetime.now(), previouse_balance=last_bal.current_balance,
transaction=self, current_balance=last_bal.current_balance + self.amount)