I'd like to ask, how I could shrink this to one command? I understand that annotate is proper way to do this,but don't understand how.
Here is my code, which is too slow:
sum = 0
for contact in self.contacts.all():
sum += (contact.orders.aggregate(models.Sum('total'))['total__sum'])
return sum
I'd like to get Sum for each contact, all records in total column of relevant orders.
Code above produces sum, but is sluggishly slow. It is my understand it can be done with annotate,but not sure how to use it.
Here is Contact:
class Contact(models.Model):
company = models.ForeignKey(
Company, related_name="contacts", on_delete=models.PROTECT)
first_name = models.CharField(max_length=80)
last_name = models.CharField(max_length=80, blank=True)
email = models.EmailField()
And here is Orders:
class Order(models.Model):
order_number = models.CharField(max_length=80)
company = models.ForeignKey(Company, related_name="orders")
contact = models.ForeignKey(Contact, related_name="orders")
total = models.DecimalField(max_digits=12, decimal_places=6)
order_date = models.DateTimeField(null=True, blank=True)
Help please
You can annotate your queryset on the Contract model with:
from django.db.models import Sum
Contract.objects.annotate(
total_orders=Sum('orders__total')
)
The Contract objects that arise from this queryset will have an extra attribute .total_orders that contains the sum of the total field of the related Order objects.
This will thus create a query that looks like:
SELECT contract.*, SUM(order.total)
FROM contract
LEFT OUTER JOIN order ON order.contract_id = contract.id
GROUP BY contract.id
Related
I am trying to create an E-Commerce Website and I am at the Final Step i.e. Placing the Order. So, I am trying to add all the Cart Items into my Shipment model. But I am getting this error.
'QuerySet' object has no attribute 'product'
Here are my models
class Product(models.Model):
productId = models.AutoField(primary_key=True)
productName = models.CharField(max_length=200)
productDescription = models.CharField(max_length=500)
productRealPrice = models.IntegerField()
productDiscountedPrice = models.IntegerField()
productImage = models.ImageField()
productInformation = RichTextField()
productTotalQty = models.IntegerField()
alias = models.CharField(max_length=200)
url = models.CharField(max_length=200, blank=True, null=True)
class Customer(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=100, null=True, blank=True)
email = models.EmailField(max_length=100)
profileImage = models.ImageField(blank=True, null=True, default='profile.png')
phoneNumber = models.CharField(max_length=10, blank=True, null=True)
address = models.CharField(max_length=500, blank=True, null=True)
class Order(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.SET_NULL, blank=True, null=True)
dateOrdered = models.DateTimeField(auto_now_add=True)
orderCompleted = models.BooleanField(default=False)
transactionId = models.AutoField(primary_key=True)
class Cart(models.Model):
product = models.ForeignKey(Product, on_delete=models.SET_NULL, blank=True, null=True)
order = models.ForeignKey(Order, on_delete=models.SET_NULL, blank=True, null=True)
quantity = models.IntegerField(default=0, blank=True, null=True)
dateAdded = models.DateTimeField(auto_now_add=True)
class Shipment(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.SET_NULL, blank=True, null=True)
orderId = models.CharField(max_length=100)
products = models.ManyToManyField(Product)
orderDate = models.CharField(max_length=100)
address = models.CharField(max_length=200)
phoneNumber = models.CharField(max_length=13)
I just removed additional functions i.e. __str__ and others.
Here is the views.py
def orderSuccessful(request):
number = Customer.objects.filter(user=request.user).values('phoneNumber')
fullAddress = Customer.objects.filter(user=request.user).values('address')
timeIn = time.time() * 1000 # convert current time in milliSecond
if request.method == 'POST':
order = Shipment.objects.create(customer=request.user.customer, orderId=timeIn,
orderDate=datetime.datetime.now(), address=fullAddress,
phoneNumber=number)
user = Customer.objects.get(user=request.user)
preOrder = Order.objects.filter(customer=user)
orders = Order.objects.get(customer=request.user.customer, orderCompleted=False)
items = orders.cart_set.all() # Here is all the items of cart
for product in items:
product = Product.objects.filter(productId=items.product.productId) # error is on this line
order.products.add(product)
Cart.objects.filter(order=preOrder).delete()
preOrder.delete()
order.save()
else:
return HttpResponse("Problem in Placing the Order")
context = {
'shipment': Shipment.objects.get(customer=request.user.customer)
}
return render(request, "Amazon/order_success.html", context)
How to resolve this error and all the cart items to field products in Shipment model?
Your model is not really consistent at all. Your Cart object is an m:n (or m2m - ManyToMany) relationship between Product and Order. Usually, you would have a 1:n between Cart and Product (a cart contains one or more products). One Cart might be one Order (unless you would allow more than one carts per order). And a shipment is usually a 1:1 for an order. I do not see any of this relationships in your model.
Draw your model down and illustrate the relations between them first - asking yourself, if it should be a 1:1, 1:n or m:n? The latter can be realized with a "through" model which is necessary if you need attributes like quantities.
In this excample, we have one or more customers placing an order filling a cart with several products in different quantities. The order will also need a shipment fee.
By the way: bear in mind that "filter()" returns a list. If you are filtering on user, which is a one to one to a unique User instance, you would better use "get()" as it returns a single instance.
Putting in into a try - except or using get_object_or_404() makes it more stable.
product = Product.objects.filter(productId=items.product.productId)
should be something like:
product = product.product
not to say, it becomes obsolete.
It looks like you make a cart for a product by multiple instances of Cart, the problem is you try to access the wrong variable, also you don't need to filter again when you already have the instance, make the following changes:
carts = orders.cart_set.all() # Renamed items to carts for clarity
for cart in carts:
product = cart.product
order.products.add(product) # The name order is very misleading makes one think it is an instance of Order, actually it is an instance of Shipment
As mentioned above in my comment your variable names are somewhat misleading, please give names that make sense to any variable.
Good afternoon,
I am really struggling with getting a sum using Annotate in DJango.
I am using User object and the following models:
class Depts(models.Model):
dept_name = models.CharField(max_length=55)
dept_description = models.CharField(max_length=255)
isBranch = models.BooleanField(default=False)
def __str__(self):
return "{}".format(self.dept_name)
class UserProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='profile')
title = models.CharField(max_length=75)
dept = models.ForeignKey(Depts, on_delete=models.CASCADE, related_name="dept", null=True)
class ActivityLog(models.Model):
activity_datetime = models.DateTimeField(default=timezone.now)
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, related_name='activity_user')
activity_category = models.ForeignKey(ActivityCategory, on_delete=models.CASCADE, null=True, related_name='activity_cat')
activity_description = models.CharField(max_length=100, default="Misc Activity")
class ActivityCategory(models.Model):
activity_name = models.CharField(max_length=40)
activity_description = models.CharField(max_length=150)
pts = models.IntegerField()
def __str__(self):
return '%s' % (self.activity_name)
What I need to do is get a group of departments with aggregating the sum of the pts earned by all the users activitylogs.
So a user is part of department, they do activities, each activity is of a type activity_category and has associated points. How can I query using the ORM to get a sum of points for everyone in each department?
Thank you, I cannot seem to wrap my mind around it.
You annotate the departments with the sum:
from django.db.models import Sum
Depts.objects.annotate(
total_pts=Sum('dept__user__activity_user__activity_category__pts')
)
Note: The related_name=… parameter [Django-doc]
is the name of the relation in reverse, so from the Depts model to the UserProfile
model in this case. Therefore it (often) makes not much sense to name it the
same as the forward relation. You thus might want to consider renaming the dept relation to userprofiles.
After setting the related_name='userprofiles', the query is:
from django.db.models import Sum
Depts.objects.annotate(
total_pts=Sum('userprofiles__user__activity_user__activity_category__pts')
)
I'm trying to query into three models. I need to get the amount products that are in the stores region location. These are my models:
class Store(models.Model):
name = models.CharField(max_length=255)
logo = models.URLField(null=True, blank=True)
user = models.OneToOneField(get_user_model(), models.CASCADE,
related_name="user_store")
region = models.CharField("Departamento", max_length=100)
city = models.CharField("Ciudad", max_length=100)
class Product(models.Model):
name = models.CharField("Nombre del Producto", max_length=255)
description = models.TextField()
stock = models.PositiveIntegerField()
seller = models.ForeignKey(User, on_delete=models.CASCADE)
Product is related with user and it is related to store, what I want to do is something like this:
{
"Risaralda": 1523,
"Cundinamarca": 8541
}
where keys "Risaralda" and "Cundinamarca" are regions and values are the product amount in these places
I tried something like this
products = Product.objects.filter(
seller__user_store__region__in=Store.objects.filter()
.values("region").distinct())
And I got products in stores regions but i need to count how many products are in every store regions
thank you a lot
Something like this should do the trick:
from django.db.models import F
from django.db.models import Count
Product.objects.annotate(region=F('seller__user_store__region')).annotate(num_products=Count('id')).values_list('region', 'num_products')
I have a class Summary:
class Summary(models.Model):
title = models.CharField(max_length=128)
category = models.ForeignKey(Category)
subcategory = models.ForeignKey(Subcategory)
content = RichTextField(null=True, blank=True)
users_rated_positive = models.ManyToManyField(
User, blank=True, related_name='summaries_rated_positive')
users_rated_negative = models.ManyToManyField(
User, blank=True, related_name='summaries_rated_negative')
author = models.ForeignKey(User, related_name='summaries_authored')
and a class UserProfile:
class UserProfile(models.Model):
user = models.OneToOneField(User, primary_key=True, related_name='profile')
karma = models.IntegerField(default=0)
rank = models.IntegerField(null=True,blank=True)
I want the karma to be calculated as the positive ratings on all the users summaries minus the negative ratings on all the users summaries
I figured I could add a property like this instead of a field:
#property
def karma(self):
summaries_list = self.user.summaries_authored.all()
positive_karma = sum(
[summary.users_rated_positive.count() for summary in summaries_list])
negative_karma = sum(
[summary.users_rated_negative.count() for summary in summaries_list])
return positive_karma - negative_karma
Is this the right way to do this? I feel like I should be using aggregate, or annotate but to be honest I'm new to Django and not entirely sure how they work for complex situations.
from django.db.models import Count
positive_karma = Summary.objects.filter(author=self.user).aggregate(pos_count=Count('users_rated_positive'))['pos_count']
negative_karma = Summary.objects.filter(author=self.user).aggregate(neg_count=Count('users_rated_negative'))['neg_count']
aggregate returns a dict so the actual value must be retrieved by the key
You would use annotate if you wanted to get the count of pos/neg ratings for every user.
I have models like this:
class Person(models.Model):
first_name = models.CharField(max_length=60)
last_name = models.CharField(max_length=60)
class Ticket(models.Model):
assigned_to = models.ForeignKey(Person, null=True, blank=True)
estimate = models.DecimalField(null=True, blank=True, decimal_places=1, max_digits=4)
I want to find the total of all estimates for all tickets that are assigned to a particular person.
I am trying to do:
for i in Person.objects.all():
Ticket.objects.filter(assigned_to=i)
and use Sum() to add estimate() for each model.
But I am getting no data with the first query. I am sure I have datas. What's wrong?
You can do this entirely in the db with an aggregate.
from django.db.models import Sum
people_with_estimates = Person.objects.all().annotate(
estimate_total=Sum('ticket__estimate'))
Now each Person has an estimate_total attribute containing the sum of all their ticket estimates.
Assuming you know the person try this:
estimate_total = Decimal(0)
ticket_list = Ticket.objects.filter(assigned_to = person.id)
for i in range(len(ticket_list)):
estimate_total += ticket_list[i].estimate