0

I'm trying to filter a model which has a DateField (date) to retrieve a queryset of instances whose date is in any one of a list of DateRanges but I'm struggling to figure out the exact logic I need.

So for example, if I have the following models:

class Period(models.Model):
    user = models.ForeignKey(User)
    range = DateRangeField()

class Game(models.Model):
    date = models.DateField()

and I've created 3 different date ranges, how do I get a list of all the Games whose date is in one of those 3 date ranges?

I know I can iterate through the ranges and use a chained Q() filter for this but I need to put this all into an annotation on a large queryset which is going to have to use a Subquery so that won't work.

My current effort looks like this:-

periods = Period.objects.filter(user__id=OuterRef('id')).values_list('range', flat=True)
games_in_periods = Game.objects.filter(date__contained_by=Subquery(periods))

but that doesn't work because the contained_by is being compared to a daterange but a queryset of dateranges.

It feels like I'm close but I must have missed something silly.

Any ideas?

M.javid
  • 6,387
  • 3
  • 41
  • 56
bodger
  • 1,112
  • 6
  • 24

2 Answers2

0

I understand your problem, documentation about Subquery is not enough clear.

Have you tried in this way?

periods = Period.objects.filter(user__id=OuterRef('id')).values('range')
games_in_periods = Game.objects.filter(date__range=[Subquery(periods)])
madeng84
  • 377
  • 1
  • 7
0

There is a more generic way to do this for people not using PostgreSQL functions.

class Period(models.Model):
    start = models.DateField()
    end = models.DateField()

class Game(models.Model):
    date = models.DateField()

Then use OuterRefs in a subquery paired with an Exists to match up games that fall between a period.

from django.db.models import OuterRef, Exists

periods = Period.objects.filter(
    start__lte=OuterRef('date'), end__gte=OuterRef('date'))

Game.objects
    .annotate(in_period=Exists(periods))
    .filter(in_period=True)
Liam M.
  • 1
  • 2