使用注释+过滤器+注释时,Django QuerySet中的重复项

时间:2016-05-11 15:44:27

标签: django django-models duplicates django-queryset

我想基于所有相关对象和过滤子集来注释查询集。假设我们有一些书籍,它们在一些商店以某种价格出售。现在,对于一本书,我想得到所有出售该书的商店,这些商店中书籍的价格以及每个商店中书籍的平均价格。

我的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

平均价格是正确的。但由于某种原因,这本书的价格已经乘以商店的总书数。我究竟做错了什么?我应该如何获得我追求的那种查询集?

2 个答案:

答案 0 :(得分:0)

一种解决方法:由于书籍和商店组合是独一无二的,因此无论是使用SumAvgMinMax还是其他相似内容都无关紧要获得特定书籍的价格,因为应该只有一个项目。但是,使用Sum对奇怪的重复项进行求和,因此最好使用MinMax来获取正确的整数,因为它们在存在重复项时会给出正确的答案:

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)

一种可能性是使用CaseWhen对注释进行过滤:

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