为类别中的每位作者选择页数最少的书

时间:2019-05-11 12:58:07

标签: django django-models django-queryset django-postgresql

这些是我的模特:

class Author(models.Model):
    name = models.CharField()

class Category(models.Model):
    name = models.CharField()

class Book(models.Model):
    pages = IntegerField()
    author = ForeignKey(Author)
    category = ForeignKey(Category)

如何为每位作者选择一本书,该书在特定类别中的页面数最少?然后按页数排序

如果有两本书的页数相同,请选择其中任意一本书。 如果作者没有该类别的书籍,则可以忽略该作者。

谢谢

2 个答案:

答案 0 :(得分:0)

您可以使用以下查询:

books = Book.objects.values('author__name')\
            .filter(category=selected_category)\
            .annotate( min_pages=Min('pages'))\
            .order_by('min_pages')

这里是测试方式:

tests.py

class SampleTestCase(TestCase):
    def setUp(self) -> None:
        a = Author.objects.create(name='a')
        b = Author.objects.create(name='b')
        c = Author.objects.create(name='c')  # Without a book in c1

        c1 = Category.objects.create(name='1')
        c2 = Category.objects.create(name='2')

        Book.objects.create(pages=20, author=a, category=c1)
        Book.objects.create(pages=10, author=a, category=c1)
        Book.objects.create(pages=15, author=a, category=c1)
        Book.objects.create(pages=10, author=a, category=c1)
        Book.objects.create(pages=10, author=b, category=c1)
        Book.objects.create(pages=5, author=b, category=c1)
        Book.objects.create(pages=7, author=b, category=c1)
        Book.objects.create(pages=1, author=a, category=c2)
        Book.objects.create(pages=1, author=b, category=c2)
        Book.objects.create(pages=1, author=c, category=c2)

    def test_queryset(self) -> None:
        selected_category = Category.objects.get(pk=1)
        books = Book.objects.values('author__name')\
            .filter(category=selected_category)\
            .annotate( min_pages=Min('pages'))\
            .order_by('min_pages')
        for book in books:
            print(book)
        # {'author__name': 'b', 'min_pages': 5}
        # {'author__name': 'a', 'min_pages': 10}

更新: 如果您需要获取Book对象-不确定,但是我认为不可能在1个查询中进行。这是您在2中可以做到的方式:

selected_category = Category.objects.get(pk=1)
ids = Book.objects.values('author__pk') \
    .filter(category=selected_category) \
    .annotate(min_pages=Min('pages')) \
    .annotate(id=Min('id'))\
    .values('id')\
    .order_by('min_pages')

books = Book.objects.filter(pk__in=ids)
for book in books:
    print(book)
# Book object (1)
# Book object (5)

答案 1 :(得分:0)

您应该使用django的annotate()子句来确定最少的页面数。然后,您可以将order_by()添加到QuerySet中。 检出:https://docs.djangoproject.com/en/2.2/topics/db/aggregation/

尝试一下:

{{1}}