Django models 带有特殊子句的Django过滤器ManyToManyField

Django models 带有特殊子句的Django过滤器ManyToManyField,django-models,many-to-many,django-1.8,Django Models,Many To Many,Django 1.8,我在Django 1.8下。我有几个播放列表,每个列表包含几个视频。其中一些视频的状态为“已删除”或“错误” 我只想要包含状态为“在线”的视频的播放列表。 class Video(models.Model): # Title of the video title = models.CharField(max_length=100) # Status of video (error, online, deleted) status = models.Cha

我在Django 1.8下。我有几个播放列表,每个列表包含几个视频。其中一些视频的状态为“已删除”或“错误”

我只想要包含状态为“在线”的视频的播放列表。

class Video(models.Model):  

    # Title of the video
    title = models.CharField(max_length=100)


    # Status of video (error, online, deleted)
    status = models.CharField(max_length=20, default="online")


class UserPlaylist(models.Model):  

    # Name of the playlist
    name = models.CharField(max_length=500, blank=True, null=True)

    # Playlist owner
    owner = models.ForeignKey(User)

    # videos
    videos = models.ManyToManyField(Video, null=False)
我的尝试没有成功:

UserPlaylist.objects.filter(videos__status="online")
UserPlaylist.objects.filter(videos__status="online").distinct()
工作但痛苦:

UserPlaylist.objects.exclude(videos__status='error').exclude(videos__status='deleted').distinct()

我们可以使用
status='online'
统计
视频的数量,并检查这是否与视频总数相同,如:

from django.db.models import Case, F, Q, When

UserPlaylist.objects.annotate(
    nvideo=Count('videos'),
    nonline=Count(Case(
        When(videos__status='online', then='videos'),
        default=None
    ))
).filter(
    nvideo=F('nonline')
)
这将生成一个如下所示的查询:

SELECT userplaylist.id, userplaylist.name,
       COUNT(userplaylist_videos.video_id) AS nvideo,
       COUNT(CASE WHEN video.status = online
             THEN userplaylist_videos.video_id
             ELSE NULL END) AS nonline
FROM userplaylist
LEFT OUTER JOIN userplaylist_videos
             ON userplaylist.id = userplaylist_videos.userplaylist_id
LEFT OUTER JOIN video ON userplaylist_videos.video_id = video.id
GROUP BY userplaylist.id
HAVING COUNT(userplaylist_videos.video_id) = COUNT(
    CASE WHEN video.status = online
    THEN userplaylist_videos.video_id
    ELSE NULL END)

请注意,不包含视频的
UserPlaylist
也将是结果的一部分,因为它的所有视频都是在线的。

我们可以使用
status='online'
计算
视频的数量,并检查这是否与总视频的数量相同,如:

from django.db.models import Case, F, Q, When

UserPlaylist.objects.annotate(
    nvideo=Count('videos'),
    nonline=Count(Case(
        When(videos__status='online', then='videos'),
        default=None
    ))
).filter(
    nvideo=F('nonline')
)
这将生成一个如下所示的查询:

SELECT userplaylist.id, userplaylist.name,
       COUNT(userplaylist_videos.video_id) AS nvideo,
       COUNT(CASE WHEN video.status = online
             THEN userplaylist_videos.video_id
             ELSE NULL END) AS nonline
FROM userplaylist
LEFT OUTER JOIN userplaylist_videos
             ON userplaylist.id = userplaylist_videos.userplaylist_id
LEFT OUTER JOIN video ON userplaylist_videos.video_id = video.id
GROUP BY userplaylist.id
HAVING COUNT(userplaylist_videos.video_id) = COUNT(
    CASE WHEN video.status = online
    THEN userplaylist_videos.video_id
    ELSE NULL END)

请注意,不包含视频的
UserPlaylist
也将是结果的一部分,因为它的所有视频都是在线的。

该尝试有什么不起作用?如果
UserPlaylist
至少包含一个状态为
online
的视频,则
UserPlaylist
将位于该查询集中。也许这不是你想要的?@WillemVanOnsem它会返回所有视频的列表,不管状态如何。我尝试使用distinct,它会返回我所有的播放列表,但里面仍然有状态不同于在线的视频。我只想要一个只有播放列表的查询集,里面的所有视频都是“在线”的。我可以添加一个排除过滤器,但它似乎非常不适合。不包括(VixOsSyStase=‘错误’)。排除(VixOsSyStase=“已删除”),虽然我认为它可以被解决(用这个答案),但是你应该考虑升级。Django-1.8不再受支持,2018年4月。这种尝试有什么不起作用?如果
UserPlaylist
至少包含一个状态为
online
的视频,则
UserPlaylist
将位于该查询集中。也许这不是你想要的?@WillemVanOnsem它会返回所有视频的列表,不管状态如何。我尝试使用distinct,它会返回我所有的播放列表,但里面仍然有状态不同于在线的视频。我只想要一个只有播放列表的查询集,里面的所有视频都是“在线”的。我可以添加一个排除过滤器,但它似乎非常不适合。不包括(VixOsSyStase=‘错误’)。排除(VixOsSyStase=“已删除”),虽然我认为它可以被解决(用这个答案),但是你应该考虑升级。Django-1.8不再受支持,2018年4月。学到了很多,不知道什么时候/案例。谢谢!学到了很多,不知道什么时候/情况。谢谢!