【问题标题】:Django Filter ManyToManyField with particular clauseDjango 使用特定子句过滤 ManyToManyField
【发布时间】:2019-08-31 10:08:38
【问题描述】:

我在 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()

【问题讨论】:

  • 这种尝试有什么问题?如果UserPlaylist 包含至少一个状态为online 的视频,那么UserPlaylist 将在该查询集中。也许这不是你想要的?
  • @WillemVanOnsem 无论状态如何,它都会返回所有视频的列表。我尝试使用 distinct 将所有播放列表返回给我,但内部仍然有状态为在线以外的视频。我只想要一个仅包含播放列表的查询集,其中所有视频都“在线”。
  • 我可以添加一个排除过滤器,但它看起来很不舒服。类似 .exclude(videos__status='error').exclude(videos__status='deleted')
  • 虽然我认为可以解决(用这个答案),但你真的应该考虑升级。 2018 年 4 月,不再支持 Django-1.8。

标签: django-models many-to-many django-1.8


【解决方案1】:

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

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)

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

【讨论】:

  • 学到了很多,不知道什么时候/案例。谢谢!
猜你喜欢
  • 1970-01-01
  • 2020-02-01
  • 2022-11-25
  • 1970-01-01
  • 2020-08-09
  • 2023-03-05
  • 2019-12-04
  • 2018-07-25
  • 2021-06-16
相关资源
最近更新 更多