我想基于所有相关对象和过滤子集来注释查询集。假设我们有一些书籍,它们在一些商店以某种价格出售。现在,对于一本书,我想得到所有出售该书的商店,这些商店中书籍的价格以及每个商店中书籍的平均价格。
我的models.py:
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=100)
class Store(models.Model):
name = models.CharField(max_length=100)
books = models.ManyToManyField(Book, through='BookInStore')
class BookInStore(models.Model):
book = models.ForeignKey(Book)
store = models.ForeignKey(Store)
price = models.IntegerField()
class Meta:
unique_together = ('book', 'store')
创建一些对象:
book1 = Book.objects.create(title='book1')
book2 = Book.objects.create(title='book2')
book3 = Book.objects.create(title='book3')
store = Store.objects.create(name='store')
BookInStore.objects.create(book=book1, store=store, price=10)
BookInStore.objects.create(book=book2, store=store, price=100)
BookInStore.objects.create(book=book3, store=store, price=1000)
现在,对于book1,我正在尝试过滤出售book1的商店,获取这些商店的价格以及每个商店中所有书籍的平均价格:
book_availability = (
Store.objects
.annotate(avg_price=Avg('bookinstore__price'))
.filter(bookinstore__book=book1)
.annotate(
duplicates=Count('bookinstore__price'),
book_price=Sum('bookinstore__price')
)
)
但是,它无法正常工作:
for b in book_availability:
print("Avg price:", b.avg_price)
print("Number of copies (should be 1):", b.duplicates)
print("Price of book1 (should be 10):", b.book_price)
我得到以下输出:
Avg price: 370.0
Number of copies (should be 1): 3
Price of book1 (should be 10): 30
平均价格是正确的。但由于某种原因,这本书的价格已经乘以商店的总书数。我究竟做错了什么?我应该如何获得我追求的那种查询集?
答案 0 :(得分:0)
一种解决方法:由于书籍和商店组合是独一无二的,因此无论是使用Sum
,Avg
,Min
,Max
还是其他相似内容都无关紧要获得特定书籍的价格,因为应该只有一个项目。但是,使用Sum
对奇怪的重复项进行求和,因此最好使用Min
或Max
来获取正确的整数,因为它们在存在重复项时会给出正确的答案:
book_availability = (
Store.objects
.annotate(avg_price=Avg('bookinstore__price'))
.filter(bookinstore__book=book1)
.annotate(
duplicates=Count('bookinstore__price'),
book_price=Min('bookinstore__price')
)
)
立即打印:
for b in book_availability:
print("Avg price:", b.avg_price)
print("Number of copies (should be 1):", b.duplicates)
print("Price of book1 (should be 10):", b.book_price)
我们现在看到正确的书价:
Avg price: 370.0
Number of copies (should be 1): 3
Price of book1 (should be 10): 10
这种解决方法恰好给出了正确的答案,但它并没有删除重复项,所以我仍然想知道这种方法是否有问题。
答案 1 :(得分:0)
一种可能性是使用Case
和When
对注释进行过滤:
book_availability = (
Store.objects
.annotate(
avg_price=Avg('bookinstore__price'),
book_price=Sum(Case(When(
bookinstore__book=book1,
then='bookinstore__price'
)))
)
.filter(bookinstore__book=book1)
)
for b in book_availability:
print("Avg price:", b.avg_price)
print("Price of book1 (should be 10):", b.book_price)
给出了:
Avg price: 370.0
Price of book1 (should be 10): 10