英文:
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
),
)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论