So I basically have this simple model:
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()
There is a many-to-many relationship with the User model in shown_users
And I wanna annotate the is_shown status based on the many-to-many table, so I did this:
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)
The problem with this is that if user1 and user2 saw the same lesson it will be duplicate, for example:
+-----------------+-----------+
| lesson_id | user_id |
+-----------------+-----------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
+-----------------+-----------+
For such case, I will get these duplicated lessons:
{
"id": 1
"type": "0",
"is_shown": true
},
{
"id": 1
"type": "0",
"is_shown": false
},
{
"id": 1
"type": "0",
"is_shown": false
}
So it's checking each related lesson field in the SHOWN_USERS table... sample photo: https://imgur.com/GJCPWjk
I added an exclude expression to get rid of the extra lessons:
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))
And I think this is super ugly cuz if I have 1000 users and 50 lessons it means I'm taking all 50000 fields then filter 50 of them :(
Is there any cleaner way to do that ?
I have tried distinct before and it's not fixing the problem, instead of shown the lesson three times it will show:
I managed to fix the problem, and If figured out that my way of querying many-to-many is the problem, instead of shown_users__id == user.id
I used id__in==user.lessons.values('id')
, full code:
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With