所以我基本上都有这个简单的模型:
class BaseLesson(models.Model):
YOUTUBE_VIDEO = '0'
MARKDOWN = '1'
TYPE_CHOICES = (
(YOUTUBE_VIDEO, 'youtube-video'),
(MARKDOWN, 'markdown'),
)
type = models.CharField(
max_length=10, choices=TYPE_CHOICES, default=MARKDOWN, verbose_name=_('type'))
shown_users = models.ManyToManyField(
User, related_name='lessons', verbose_name=_('shown users'), blank=True)
objects = managers.BaseLessonManager()
在shown_users
中与用户模型存在多对多关系我想基于多对多表来注释is_shown状态,所以我这样做了:
class BaseLessonManager(InheritanceManager, CachingManager):
def get_lesson_with_is_shown(self, user):
shown_user_case = django_models.Case(
django_models.When(shown_users__id=user.id,
then=django_models.Value(True)),
default=django_models.Value(False),
output_field=django_models.BooleanField())
return self.get_queryset().annotate(
is_shown=shown_user_case)
问题在于,如果user1和user2看到相同的课程,那么它将是重复的,例如:
+-----------------+-----------+
| lesson_id | user_id |
+-----------------+-----------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
+-----------------+-----------+
对于这种情况,我会得到这些重复的课程:
{
"id": 1
"type": "0",
"is_shown": true
},
{
"id": 1
"type": "0",
"is_shown": false
},
{
"id": 1
"type": "0",
"is_shown": false
}
所以它在SHOWN_USERS表中检查每个相关课程字段 ...示例照片: https://imgur.com/GJCPWjk
我添加了一个排除表达式来摆脱额外的教训:
return self.get_queryset().annotate(
is_shown=shown_user_case).exclude(
django_models.Q(is_shown=False) & django_models.Q(shown_users__id=user.id))
我认为这是超级丑陋的,因为如果我有1000个用户和50个课程,这意味着我将获得所有50000个字段,然后过滤其中的50个:(
有没有更清洁的方法呢?
之前我曾尝试过不同的方法而且没有解决问题,而是将这一课程显示三次:
如果你想尝试,这是github回购: https://github.com/coretabs-academy/website-v2
答案 0 :(得分:0)
我设法解决了这个问题,如果发现我查询多对多的方式就是问题,那么我会使用shown_users__id == user.id
而不是id__in==user.lessons.values('id')
来编写完整代码:
class BaseLessonManager(InheritanceManager, CachingManager):
def with_is_shown(self, user):
user_shown_lessons = user.lessons.values('id')
shown_user_case = django_models.Case(
django_models.When(id__in=user_shown_lessons,
then=django_models.Value(True)),
default=django_models.Value(False),
output_field=django_models.BooleanField())
return self.get_queryset().annotate(
is_shown=shown_user_case)