0

Consider the following condition:

class Book(models.Model):
    name = models.CharField(max_length=300)
    price = models.IntegerField(default=0)

    def __str__(self):
        return self.name


class Store(models.Model):
    name = models.CharField(max_length=300)
    default = models.BooleanField(default=False)
    books = models.ForeignKey(Book, on_delete=models.CASCADE)

So, for this query:

Book.objects.prefetch_related(Prefetch('store_set',
            queryset=Store.objects.filter(default=False)))
            .values("store__name", "name", "store__default")

The SQL query is not considering queryset default=True condition

SELECT "core_store"."name",
       "core_book"."name",
       "core_store"."default"
  FROM "core_book"
  LEFT OUTER JOIN "core_store"
    ON ("core_book"."id" = "core_store"."books_id")

Result:

<QuerySet [{'store__name': 'Subway Store', 'name': 'Hello', 'store__default': False}, 
{'store__name': 'Times Square', 'name': 'Hello', 'store__default': False}, 
{'store__name': 'Subway Store', 'name': 'GoodBye', 'store__default': True}, 
{'store__name': 'Times Square', 'name': 'GoodBye', 'store__default': False}, 
{'store__name': 'Subway Store', 'name': 'Greetings', 'store__default': True}, 
{'store__name': 'Subway Store', 'name': 'HateWords', 'store__default': False}]>

I want to have a query set condition while prefetching the query. I am not able to find any way to do it in one query or a minimum number of queries.

I was thinking it should make a where condition with the OUTER JOIN with core_store table. Here

LEFT OUTER JOIN "core_store"
    ON ("core_book"."id" = "core_store"."books_id")
Nishant Kashyap
  • 819
  • 2
  • 15
  • 25

1 Answers1

0

If you want only the values as stated in your question then you can directly use fk relationship from the Store model to Book model as below. No need to do reverse lookup.

Store.objects.filter(default=True).values('name', 'default', 'books__name')

But if u want to fetch all Books and also their corresponding Store then you can use only on prefetch_related. .values does not work with prefetch_related as stated in the docs. You can also use a for loop after assigning a to_attr in your prefetch_related query. The for loop does not do a db query. Instead it gets the value from prefetched results.

books_stores = Book.objects.prefetch_related(Prefetch('store_set',
            queryset=Store.objects.filter(default=False), 
            to_attr='stores'))


for book_store in book_stores:
    stores = book_store.stores
Lax_Sam
  • 1,099
  • 2
  • 14
  • 32