0

I currently have the following code that I want to optimize using select_related. The aim is to find out the set of BaseReward for which there is at least one Voucher with is_active = True

    class Basereward(models.Model):
        active = models.BooleanField()

    class Voucher(models.Model):
        reward = models.ForeignKey(BaseReward, related_name='reward_vouchers')
        is_active = models.BooleanField()

View

    qs = BaseReward.objects.filter(active=True).all()
    for reward in qs:
        if not reward.reward_vouchers.filter(is_active=True).all():
            qs = qs.exclude(id=reward.id)
    return qs

What is the correct way of doing it? I was thinking using the select_related using the reverse relationship, but the doc says it won't work Any other way?

    qs = BaseReward.objects.filter(active=True).all().select_related(reward_vouchers)
dowjones123
  • 3,695
  • 5
  • 40
  • 83

1 Answers1

1

Do it the other way around, get the unique set of BaseReward objects for where there is at least one Voucher:

Voucher.objects.filter(is_active=True).distinct(reward)
Burhan Khalid
  • 169,990
  • 18
  • 245
  • 284
  • Thank you, in fact I need the final queryset returned (qs) to be a list of BaseReward objects. Current what you put would give me Voucher objects. Should I just add it at end: Voucher.objects.filter(is_active=True).distinct(reward).reward.all()? – dowjones123 Sep 20 '14 at 15:38
  • All `Voucher` objects will have a `voucher.reward` object, which represents the `BaseReward` object for that voucher; not to mention what you wrote `distinct(reward).reward.all()` is not valid; if all you want is the reward object, try `distinct('reward').values('reward')`, see the documentation for [`values`](https://docs.djangoproject.com/en/1.4/ref/models/querysets/#values) for more information. – Burhan Khalid Sep 20 '14 at 16:31
  • Thanks Burhan. I tried distinct('reward').values('reward') and it gave me : [{'reward': 3}, {'reward': 4}, {'reward': 5}, {'reward': 6}, {'reward': 7}, {'reward': 8}, {'reward': 11}] What I am looking for is a queryset that has BaseReward object, as I am writing this code in the get_queryset() method of a REST framework Viewset – dowjones123 Sep 20 '14 at 21:06
  • Ah okay, so you need a `BaseReward` queryset - you'll need to build it yourself, try `BaseReward.objects.filter(pk__in=[i.reward.pk for i in Voucher.objects.filter(is_active=True).distinct(reward)])`. I think this is the best you can do, short of fetching all `BaseRewards` and then filtering each that has a `voucher_set.exists()`. – Burhan Khalid Sep 20 '14 at 21:19