sunn0
sunn0

Reputation: 3046

Filter on ManyToManyField only if relations exist in one query

I am trying to merge the two count queries in the below visible function into one query. Function should return True if there are no relations or if there are relations and some specific filtering is true.

class OtherModel(models.Model):
    starts = models.DateField()
    ends = models.DateField()

class MyModel(models.Model):

    m2m = models.ManyToManyField('OtherModel', blank=True, )

    def visible(self):
        # Should always return True if no relations to OtherModel are present.

        if self.m2m.exists(): 
            # If relations to OtherModel are present check for starts and ends.
            # The reason for the first check is that if there are no relations
            # and the below query returns 0 the function will return False 
            today = datetime.date.today()
            return self.m2m.filter(starts__lte=today, ends__gte=today).exists()
        return True

EDIT: More code and comments, replaced count with exists.

The m2m relation is for date restrictions but if no date restrictions are available the function should return True (object visible if no restrictions at all but not visible if restrictions present but not matching current date).

The sample code is just a simplified example, I will need to do this and actually return querysets as well.

Upvotes: 0

Views: 2891

Answers (2)

sunn0
sunn0

Reputation: 3046

Solved it with Q and aggregate, thanks Aamir for exists():

def visible(self):
    today = datetime.date.today()
    return MyModel.objects.annotate(num_m2m=Count('m2m')).filter(
            Q(pk=self.pk) &
            (
                Q(m2m__starts__lte=today) &
                Q(m2m__ends__gte=today) |
                Q(num_m2m=0)
            )
    ).exists()

Upvotes: 3

Aamir Rind
Aamir Rind

Reputation: 39669

Not sure exactly what are your actual requirements but I think you want to avoid self.m2m.count() > 0 extra check and wanted to return a boolean value from function if it exists or not, then you need .exists which will return True if the QuerySet contains any results, and False if not:

def visible(self):
    # return True or False
    return self.m2m.filter(some_filter=some_value).exists()

Upvotes: 1

Related Questions