gibbe75
gibbe75

Reputation: 33

django query model A and exclude some items from related model B

I'm new to Django and I'm facing a question to which I didn't an answer to on Stackoverflow. Basically, I have 2 models, Client and Order defined as below:

class Client(models.Model):
  name = models.CharField(max_length=200)
  registration_date = models.DateTimeField(default=timezone.now)
  # ..


class Order(models.Model):
  Client = models.ForeignKey(ModelA, on_delete=models.CASCADE, related_name='orders')
  is_delivered = models.BooleanField(default=False)
  order_date = models.DateTimeField(default=timezone.now)
  # ..        

I would like my QuerySet clients_results to fulfill the 2 following conditions:

I could do this directly in the template but I feel this is not the correct way to do it. Additionally, I read in the doc that Base Manager from Order shouldn't be used for this purpose. Finally, I found a question relatively close to mine using Q and F, but in the end, I would get the order_id while, ideally, I would like to have the whole object.

Could you please advise me on the best way to address this need?

Thanks a lot for your help!

Upvotes: 2

Views: 418

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476584

You probably should use a Prefetch(..) object [Django-doc] here to fetch the related non-delivered Orders for each Client, and stores these in the Clients, but then in a different attribute, since otherwise this can generate confusion.

You thus can create a queryset like:

from django.db.models import Prefetch
from django.utils.timezone import now
from datetime import timedelta

last_six_weeks = now() - timedelta(days=42)

clients_results = Client.objects.filter(
    name__startswith='d'
).prefetch_related(
    Prefetch(
        'orders',
        Order.objects.filter(is_delivered=False, order_date__gte=last_six_weeks),
        to_attr='nondelivered_orders'
    )
)

This will contain all Clients where the name starts with 'd', and each Client object that arises from this queryset will have an attribute nondelivered_orders that contains a list of Orders that are not delivered, and ordered in the last 42 days.

Upvotes: 2

Related Questions