简体   繁体   中英

Django filter models related to models in a QuerySet

I have Book and Review models in my app, Review has foreign key field pointing to Book .

from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from datetime import datetime


 class Book(models.Model):
    title = models.CharField(max_length=300, null=False, blank=False)
    category = models.CharField(max_length=40, blank=True)

class Review(models.Model):
    reviewer = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
    book = models.ForeignKey(Book, on_delete=models.CASCADE, null=False)
    date_last_modified = models.DateTimeField(null=True)
    is_closed = models.BooleanField(default=False)

Now, by applying some filters I have a QuerySet of Books (eg. books from certain category ).

books_qs = Book.objects.filter(category='World War II') 

Having this QuerySet I want to find most recent Review with is_closed=True for each Book up to any given date. The simplest solution is of course:

desired_date = datetime(2018, 2, 12)

reviews = []
for book in books_qs:
    try:
        latest = book.review_set.filter(date_last_modified__lt=desired_date, is_closed=True).latest('date_last_modified')
    except Book.DoesNotExist:  # latest() throws this exception if Book has no reviews
        continue
    else:
        reviews.append(latest)

Is there a way to improve this to get all Reviews in one query, instead of doing it multiple times in a loop for each Book ?

Another way of phrasing your problem is that you'd like to filter on all reviews whose book is categorized by "World War II", was modified before desired_date , and is_closed and group by book and take the most recent review.

So we'll filter our reviews by exactly those criteria. We'll then order the queryset by book and date_last_modified so that when we take distinct values on book , they'll be sorted by the field we want to take the "first" of in each group.

Review.objects.filter(
    date_last_modified__lt=desired_date, is_closed=True,
    book__category='World War II',
).order_by("book", "-date_last_modified").distinct("book")

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