简体   繁体   中英

Count and group by foreign key and return model django

Data Model:

  • Each Library has many Book s
  • Each Book may or may not be read by a User dictated by the UserBookReceipt

Objective:

Given a user, order the libraries by how many books they have read at each library.

Example:

User 1 has read:

  • 2 Books from Library 1
  • 5 Books from Library 2
  • 1 Book from Library 3
  • 0 Books from Library 4

We should return the libraries in order of number of books read by User 1 ie:

Library 2, Library 1, Library 3

Current solution

libraries = (UserBookReceipt.objects
    .filter(user=user)
    .values('book__library')
    .annotate(rcount=Count('book__library'))
    .order_by('-rcount')
)

Actual output

[
    {'book_library': 2, 'rcount': 5}, 
    {'book_library': 1, 'rcount': 2}, 
    {'book_library': 3, 'rcount': 1}
]

Expected output

[
    {'book_library': <Library: 2>, 'rcount': 5}, 
    {'book_library': <Library: 1>, 'rcount': 2}, 
    {'book_library': <Library: 3>, 'rcount': 1}
]

The actual output is 90% of what I want, except I want the book_library value to be instances of the django Library model rather than just the library id. Otherwise, I have to make another query to retrieve the Library objects which would probably require some inefficient ids__in query .

How can I count and group by the UserBookReceipt.book__library and return the Library model?

If I were to do this in SQL the query would look like

with rcount_per_lib as (
    select lib.id, count(*) as rcount
    from lib, books, user_book_receipts
    where user_book_receipts.book_id = books.id
        and lib.id = books.lib_id 
        and user_book_receipts.user_id = 1
    group by lib.id)
select lib.*, rcount from rcount_per_lib 
where lib.id = rcount_per_lib.id 
order by rcount

You need to change how you approach the queryset. Use Library rather than UserBookReceipt .

libraries = (Library.objects
    .filter(book__userbookreceipt__user=user)
    .annotate(rcount=Count('book__userbookreceipt', distinct=True))
    .order_by('-rcount')
)
[x.rcount for x in libraries]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM