In a Django QuerySet, how to filter for “not exists” in a many-to-one relationship

后端 未结 7 1927
失恋的感觉
失恋的感觉 2020-12-23 19:23

I have two models like this:

class User(models.Model):
    email = models.EmailField()

class Report(models.Model):
    user = models.ForeignKey(User)
         


        
7条回答
  •  感动是毒
    2020-12-23 19:30

    New in Django 1.11 you can add EXISTS subqueries:

    User.objects.annotate(
        no_reports=~Exists(Reports.objects.filter(user__eq=OuterRef('pk')))
    ).filter(
        email__startswith='a',
        no_reports=True
    )
    

    This generates SQL something like this:

    SELECT
        user.pk,
        user.email,
        NOT EXISTS (SELECT U0.pk FROM reports U0 WHERE U0.user = user.pk) AS no_reports
    FROM user
    WHERE email LIKE 'a%' AND NOT EXISTS (SELECT U0.pk FROM reports U0 WHERE U0.user = user.pk);
    

    A NOT EXISTS clause is almost always the most efficient way to do a "not exists" filter.


    As of Django 3.0 you can now use expressions directly in a filter(), removing the unnecessary SQL clause:

    User.objects.filter(
        email__startswith='a',
        ~Exists(Reports.objects.filter(user__eq=OuterRef('pk')))
    )
    
    SELECT user.pk, user.email
    FROM user
    WHERE email LIKE 'a%' AND NOT EXISTS (SELECT U0.pk FROM reports U0 WHERE U0.user = user.pk);
    

提交回复
热议问题