Home > Software engineering >  Fetch relations from ManyToMany Field using Annotation
Fetch relations from ManyToMany Field using Annotation

Time:08-11

I have my database here. Where I have 2 users connected to one instance of ChatRoomParticipants with a ManyToManyField.

I'm trying to get list of related users from a ManyToMany Relation Field from ChatRoomParticipants where I don't want to show the currently authenticated user in the list with other fields i.e room present in the model.

Considering user f4253fbd90d1471fb54180813b51d610 is currently logged in and is related to all ChatRooms via ChatRoomParticipants model.

Things I've tried but couldn't get the desired output

chatrooms = list(ChatRoomParticipants.objects.filter(user=user).values_list('user__chatroom_users__user__username', 'room').distinct())

#####
chatrooms =  ChatRoomParticipants.objects.filter(user=user).annotate(user!=User(user))

####
chatrooms = Chatrooms.objects.filter(user=user)
rooms = chatrooms.values('user__chatroom_users__user__username').distinct().exclude(user__chatroom_users__user__username=user)

I want an output like

[
    {
        'user': '872952bb6c344e50b6fd7053dfa583de'
        'room': 1
    },
    {
        'user': '99ea24b12b8c400689702b4f25ea0f40'
        'room': 2
    },
    {
        'user': 'eecd66e748744b96bde07dd37d0b83b3'
        'room': 3
    },
]

models.py

class ChatRoom(models.Model):
    name = models.CharField(max_length=256)
    last_message = models.CharField(max_length=1024, null=True)
    last_sent_user = models.ForeignKey(
        User, on_delete=models.PROTECT, null=True)

    def __str__(self):
        return self.name


class Messages(models.Model):
    room = models.ForeignKey(ChatRoom, on_delete=models.PROTECT)
    user = models.ForeignKey(User, on_delete=models.PROTECT)
    content = models.CharField(max_length=1024)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.content


class ChatRoomParticipants(models.Model):
    user = models.ManyToManyField(User, related_name='chatroom_users')
    room = models.ForeignKey(ChatRoom, on_delete=models.PROTECT)

CodePudding user response:

if you still want to keep your DB design then check the bellow link for query reference

[https://docs.djangoproject.com/en/4.0/topics/db/queries/#lookups-that-span-relationships]

CodePudding user response:

I think the database design is wrong. Especially, the user field in the ChatRoomParticipants model is defined as ManyToManyField but I think it should be just as ForeignKey because there needs to be the M2M relationship between User and ChatRoom, not between User and ChatRoomParticipants.

class ChatRoomParticipants(models.Model):
    user = models.ForeignKey(User, related_name='chatroom_users', on_delete=models.PROTECT)
    room = models.ForeignKey(ChatRoom, on_delete=models.PROTECT)

Then the filter function should work.

First you need to get the list of rooms:

room_ids = list(ChatRoomParticipants.objects.filter(user=user).values_list('room__id', flat=True))

And you get the roommates:

participants = ChatRoomParticipants.objects.exclude(user__id = user.id).filter(room__id__in = room_ids)
  • Related