Am having a model called Cart which contains the following fields
class Cart(models.Model):
client = models.ForeignKey(User, null=True)
description = models.CharField(max_length = 100)
price = models.DecimalField(max_digits=10, decimal_places=2)
quantity = models.PositiveIntegerField()
ordered = models.BooleanField(default=False)
created_on = models.DateTimeField(auto_now_add = True)
def __str__(self):
return self.description
def total(self):
return self.price * self.quantity
I wish to get the total amount per item in django views.
Here is the cart views
def cart(request): # Client View
request_user = request.user
item = Cart.objects.filter(client=request_user, ordered=False).values('total')
print "item ", item
If i print item, i get
Cannot resolve keyword 'total' into field. Choices are: client, client_id, created_on, description, docfile, id, order_id, ordered, price, quantity
But if i print item when .value('price'), i get result.
Is there a way to get value of total amount
Why don't you try this? ie, access the total method through Cart instance.
[i.total() for i in Cart.objects.filter(client=request_user, ordered=False)]
You need to create total function which will take price of each object you filter on the basis of client OR you can right query as follows:
total = 0
for i in Cart.objects.filter(client=request_user, ordered=False).all():
#print i.price
total = total+i.price
You can't write values('total'), as you don't have any field in your card model as total.
Related
I don't know how to actually write the logic but I want to check user inputs especially price form field against Product model (price property).
I have model the django form as below:
class SalesForm(forms.ModelForm):
class Meta:
model = Sales
fields = ['product', 'customer', 'quantity', 'price']
Here is the Product Model
class Product(models.Model):
name = models.CharField(max_length=100, null=True)
category = models.CharField(max_length=100, choices=CATEGORY, null=True)
cost = models.PositiveIntegerField(null=True)
price = models.PositiveIntegerField(null=True)
quantity = models.PositiveIntegerField(null=True)
Here is what I am trying to do logically in views.py
def user_add_sales(request):
sales = Sales.objects.all().order_by('-salesdate')[:5]
if request.method == 'POST':
sales_form = SalesForm(request.POST)
if sales_form.is_valid:
sales_price = sales_form.cleaned_data('price')
try:
user_input = Product.objects.get(price = sales_price)
sales_form.save()
messages.success(request, 'Sales Added Successfully')
return redirect('dashboard-user-sales')
except user_input.DoesNotExist:
sales_form = SalesForm()
else:
sales_form = SalesForm()
context = {
'sales' : sales,
'sales_form' : sales_form,
}
return render(request, 'dashboard/user_sales.html', context)
When Try running the code, says 'SalesForm' object has no attribute 'cleaned_data'. Someone should please help me on how I can check whether what the user enters in price field of the SalesForm is not less than the price set for that product in Product Model.
The form is validated by the sales_form.is_valid() method, not by an attribute, the if condition is thus:
if sales_form.is_valid():
# …
# …
In your form, you can check if the given price is at least the price in the related attribute with:
class SalesForm(forms.ModelForm):
# …
def clean(self, *args, **kwargs):
data = super().clean(*args, **kwargs)
if data['price'] < data['product'].price:
raise ValidationError('The price of the sale is below the price of the product')
return data
I have an issue with database and sum in Django.
I have 3 tables: customer, order and orderLine.
For a report, I would like to calculate the sum of all line price for every order of a customer.
class Customer(models.Model):
firstName = models.CharField(max_length=200)
lastName = models.CharField(max_length=200)
mail = models.EmailField(max_length=100)
etc...
def get_count_of_orders(self):
return self.orders.count()
def get_sum_line_prince_of_all_orders(self):
???????????
return (sum_of_all_line_prince_all_orders)
class Order(models.Model):
orderNum = models.CharField(max_length=200)
customer = models.ForeignKey(Customer, related_name="orders")
globalAmount = models.DecimalField(max_digits=20, decimal_places=4)
...
class OrderLine(models.Model):
order = models.ForeignKey(Order, related_name="ordersLines")
linePrice = models.DecimalField(max_digits=20, decimal_places=4)
...
I don't know what to set in get_sum_of_orders to get the right result.
I've trayed different things like annotate or aggregate.
But without success at the moment.
I didn't understand this process at the moment.
Could you help me?
You can access all orders with:
self.orders.all()
and you can iterate them with:
sum = 0
for each_order in self.orders.all():
sum += each_order.globalAmount
return sum
Here I presume that globalAmount is the amount you need to calculate.
If you need to reach OrderLine -> linePrice through each customer and calculate the sum of the linePrice(s) of each customer, try the following:
for each_order in self.orders.all(): #each order
for each_OrderLine in each_order.ordersLines.all()
sum += each_OrderLine.linePrice
Or you could use list comprehension:
sum([myorder.linePrice for myorder in order.ordersLines.all() for order in self.orders.all()])
Suppose I have a view for saving an order to a database based on cart contents:
def cart_checkout(request):
order = Order()
order.first_name = 'x'
order.last_name = 'y'
order.address = 'z'
order.save()
cart = Cart(request)
for product_id, product_quantity in cart:
product = Product.objects.get(pk=product_id)
order_item = OrderItem()
order_item.order = order
order_item.name = product.name
order_item.price = product.price
order_item.amount = product_quantity
order_item.save()
order.update_total_price() # updates the Order total price field with the sum of order items prices
order.save()
return HttpResponse('Checked-out!')
As you can see, I am calling order.save() twice in this view: first to create an Order instance the OrderItems can be attached to in the for loop, and then to update the total price of the order based on order items in it. If I removed the first .save(), I would get an error on the second one telling me the order needs to be saved first.
Calling the .save() method twice does not seem DRY enough to me. Is there a way to do it only once?
Note that I am not subclassing ModelForm, so I cannot use .save(commit=False). Also, I do not want to just hide the save() method in the update_total_price() method.
Models.py:
from django.db import models
from .mixins import DisplayNameMixin
class Product(DisplayNameMixin, models.Model):
name = models.CharField(max_length=255)
price = models.DecimalField(max_digits=6, decimal_places=2)
amount = models.IntegerField()
class Order(models.Model):
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
address = models.CharField(max_length=255)
total_price = models.DecimalField(max_digits=10, decimal_places=2, default=0)
def update_total_price(self):
order_items = self.orderitem_set.all()
self.total_price = sum([
x.price * x.amount
for x in order_items
])
class OrderItem(models.Model):
order = models.ForeignKey('Order', on_delete=models.CASCADE)
name = models.CharField(max_length=255)
price = models.DecimalField(max_digits=6, decimal_places=2)
amount = models.IntegerField()
I think, you can't help but save the order twice, as you need to have an order_id to create the OrderItems, and then update the order with the items' amount.
I have a few suggestions to make though.
You can make total_price a calculated property, so that you would not have to save the order:
class Order(models.Model):
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
address = models.CharField(max_length=255)
total_price = models.DecimalField(max_digits=10, decimal_places=2, default=0)
#property
def total_price(self):
return sum([
x.price * x.amount
for x in self.orderitem_set.all()
])
From DB theory perspective your DB structure is wrong. It needs to be normalized first.
Why it is wrong?
Order.total_price is redundant table column. That information can be found with aggregation. At DB level there are no protections preventing DB users (Django app in your case) from entering compromised data. So your DB can be telling two different total prices (Order.total_price != SUM(OrderItem.price * OrderItem.amount)) at the same time.
So to appease DB normalization gods you need to drop total_price field and use Django aggregations/annotations (https://docs.djangoproject.com/en/3.0/topics/db/aggregation/) when you need to access it.
Saying that, there could be a totally valid reason to put total_price inside Order table. That reason usually is performance. Sometimes SQL query complexity (It is very annoying to filter by an aggregated column).
But there is a price. And that price is de-normalization of your DB. In your case you are paying breaking DRY principle.
Just make sure that you are calling both save()'s in the same transaction.
To expand on petraszd's answer (i.e. remove the total_price field) and engin_ipek's answer (i.e. add total_price as a calculated property), you could try making total_price a cached property, to avoid calculating the same value more than once - as long as the same Order instance is passed around.
You would also probably make the calculation a little less expensive if you used aggregation to calculate the total price, as petraszd mentioned, e.g. adding the products of price and amount.
I am trying to store the total attendance of the student in percentage to the database without form.
the Views.py
def attStudName(request):
#to display each student name with their total mark in table form
students = MarkAtt.objects.values('studName__VMSAcc').annotate(mark=Sum('attendance'))
#to convert the total mark to percentage and save in FinalAtt table
mark = 0
mark += students.attendance
work = FinalAtt.objects.all()
for stud in students:
stud.mark = (stud.mark/1100) * 100
work.VMSAcc = students
work.finalMark = mark
work.save()
context = {
'students' : students
}
return render(request,'show-name.html',context)
MarkAtt Model:
class MarkAtt(models.Model):
studName = models.ForeignKey(Namelist,on_delete=models.SET_NULL,blank=True, null=True, default=None)
classGrp = models.ForeignKey(GroupInfo, on_delete=models.SET_NULL, null=True)
currentDate = models.DateField(default=now())
week = models.IntegerField(default=1)
attendance = models.IntegerField(default=100)
FinalAtt Model:
class FinalAtt(models.Model):
VMSAcc= models.ForeignKey(Namelist, on_delete=models.SET_NULL, null=True)
finalMark = models.DecimalField(max_digits=5, decimal_places=2)
The error i am getting is:
'QuerySet' object has no attribute 'attendance'
How do i resolve this and save the information i want successfully?
students is a QuerySet, so you can't do mark += students.attendance.
You most likely want to loop through them to calculate mark.
Cart_Items , Cart , Order models .i want for my shopping website , that after a ordering a product , a value increased , but i cant access it .
i tried to get it by
pr=Products.objects.all()
ci=Cart_Items.filter(product_id=pr)
my models.py:
order model:
class Order(models.Model):
user = models.ForeignKey(User,blank=True,null=True,on_delete=models.CASCADE)
order_id = models.CharField( max_length=120,default="ABC",unique=True)
cart = models.ForeignKey(Cart,on_delete=models.CASCADE)
statu = models.CharField(max_length=120,choices=STATUS_CHOICES,default="Started")
product model:
class Products(models.Model):
title = models.CharField(max_length=250)
order_qty = models.IntegerField(default=0)
Cart_Items model:
class Cart_Items(models.Model):
cart = models.ForeignKey('Cart',null=True,blank=True,on_delete=models.CASCADE)
product_id = models.ForeignKey(Products,null=True,blank=True,on_delete=models.CASCADE)
cart model :
class Cart(models.Model):
total = models.IntegerField(default=0)
timestamp = models.DateTimeField(auto_now_add=True,auto_now=False)
date = models.DateTimeField(auto_now_add=False,auto_now=True)
isPaid = models.BooleanField(default=False)
my views.py:
def add(request):
cart_items=Cart_Items.objects.all()
for item in cart_items:
print(item.product_id.order_qty)
item.product_id.order_qty +=1
render(request,"home.html",{})
i want after ordering a product , order_qty , increased , how can i do that ?
i must do it by Cart_items ? or there is another ways ?
plz help.
You have to call save method, after you change a value:
def add(request):
cart_items=Cart_Items.objects.all()
for item in cart_items:
print(item.product_id.order_qty)
item.product_id.order_qty +=1
item.save()
render(request,"home.html",{})
Or like #Wilem said, you can count the quantity by the number of all the items in the order:
Order.objects.all().count