QuerySet中使用多个ManyToMany字段查找不如预期的那样运行。

huangapple go评论63阅读模式
英文:

QuerySet filter with multiple ManyToMany field lookups does not behave as expected

问题

I am trying to write a simple Django filter which doesn't seem to work as expected. I have a User model and a Vacation model and I want to write a query to get all users that are not on vacation.

class User(models.Model):
    vacation = models.ManyToManyField('Vacation')


class Vacation(models.Model):
    start = models.DateTimeField()
    end = models.DateTimeField()

The obvious solution I tried was:

now = timezone.now()

User.objects.exclude(vacation__start__lte=now, vacation__end__gt=now)

This doesn't work. This queryset will also exclude any User objects that have a future vacation (start time and end time after now). I think this is a result of the combination of the exclude and the many-to-many lookup, but I can't find any documentation on why this behaves this way, and there doesn't seem to be a way to get the simple result using a simple queryset chain. The only way I've got this to work is by querying User objects on vacation and then running another queryset excluding users with those IDs. Ex:

users_on vacation = User.objects.filter(vacation__end__gte=now, vacation__start__lte=now).values_list('pk', flat=True)
users_not_on_vacation = User.objects.exclude(pk__in=users_on_vacation)

Is there a simpler way to write this filter?

英文:

I am trying to write a simple Django filter which doesn't seem to work as expected. I have a User model and a Vacation model and I want to write a query to get all users that are not on vacation.

class User(models.Model):
    vacation = models.ManyToManyField('Vacation')


class Vacation(models.Model):
    start = models.DateTimeField()
    end = models.DateTimeField()

The obvious solution I tried was:

now = timezone.now()

User.objects.exclude(vacation__start__lte=now, vacation__end__gt=now)

This doesn't work. This queryset will also exclude any User objects that have a future vacation (start time and end time after now). I think this is a result of the combination of the exclude and the many to many lookup but I can't find any documentation on why this behaves this way and there doesn't seem to be a way to get to the simple result using simple queryset chain. The only way I've got this to work is by querying User objects on vacation and then running another queryset excluding users with those IDs. Ex:

users_on_vacation = User.objects.filter(vacation__end__gte=now, vacation__start__lte=now).values_list('pk', flat=True)
users_not_on_vacation = User.objects.exclude(pk__in=users_on_vacation)

Is there a simpler way to write this filter?

答案1

得分: 1

以下是翻译好的部分:

请查看 https://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships(尤其是第二个注意事项)。您将会看到,在将条件应用于多对多集合的“每个”而不是“任何”时,exclude 行为稍有不同。

该页面提供的解决方法,适用于您的情况,将类似于:

User.objects.exclude(
    vacation__in=Vacation.objects.filter(
        start__lte=now,
        end__gt=now
    ),
)
英文:

Have a look at https://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships (the second note in particular). You'll see exclude behaves slightly differently to filter when applying criteria to 'each' in a m2m set rather than 'any'.

The way round provided on that page, fitted to your case, would look something like:

User.objects.exclude(
    vacation__in = Vacation.objects.filter(
         start__lte=now, 
         end__gt=now
    ),
)

huangapple
  • 本文由 发表于 2023年4月20日 08:08:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/76059648.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定