@reqww

Проблема с аннотацией?

Есть следующие модели:
class Chat(models.Model):
    participants = models.ManyToManyField(Contact, related_name='chats')
    messages = models.ManyToManyField(Message, blank=True)

    def __str__(self):
        return f'{self.pk}'

    class Meta:
        verbose_name = 'Чат'
        verbose_name_plural = 'Чаты'

Это модель чата, с определенным обратным именем для участников чата
Есть модель контакта:

class Contact(AbstractBaseUser, PermissionsMixin):
    '''Кастомная модель пользователя'''
    email = models.EmailField(verbose_name='email', max_length = 60, unique = True)
    slug = models.SlugField(default='')
    first_name = models.CharField(max_length=30, default = '')
    last_name = models.CharField(max_length=30, default = '')
    phone_number = models.CharField(max_length=11)
    date_joined = models.DateTimeField(verbose_name='date joined', auto_now_add = True)
    last_login = models.DateTimeField(verbose_name='last login', auto_now = True)
    is_admin = models.BooleanField(default=False)
    is_staff = models.BooleanField(default=False)
    is_superuser = models.BooleanField(default=False)
    avatar = models.ImageField(upload_to='user_avatars/%Y/%m/%d', blank=True)
    is_active = models.BooleanField(default=False)
    status = models.CharField(max_length=100, default='', blank=True)
    friends = models.ManyToManyField('self', blank=True)
    

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['first_name', 'last_name', 'phone_number', 'slug']

    objects = ContactManager()

    def __str__(self):
        return self.email

    def get_url(self):
        try:
            return self.avatar.url
        except ValueError:
            return None

    class Meta:
        verbose_name = 'Контакт'
        verbose_name_plural = 'Контакты'
        ordering = ['-date_joined']

Также написан менеджер:
class ContactManager(BaseUserManager):
    '''Мэнэджер кастомного пользователя'''
    def create_user(
        self,
        email,
        first_name,
        last_name, 
        phone_number, 
        slug, 
        password = None
    ):
        user = self.model(
            email=self.normalize_email(email),
            first_name=first_name.capitalize(),
            last_name=last_name.capitalize(),
            phone_number=phone_number,
            slug=slug
        )

        user.set_password(password)
        user.save(using = self._db)

        return user

    def create_superuser(
        self, 
        email, 
        first_name, 
        last_name, 
        phone_number, 
        slug, 
        password = None
    ):
        user = self.model(
            email=self.normalize_email(email),
            first_name=first_name,
            last_name=last_name,
            phone_number=phone_number,
            slug=slug
        )

        user.set_password(password)

        user.is_admin = True
        user.is_staff = True
        user.is_superuser = True
        user.is_active = True

        user.save(using = self._db)

        return user

Есть вью для вывода определенного контакта:

class ContactCustomViewSet(RetrieveUpdateDestroyPermissionViewset):
    '''Обзор, обновление и удаление контакта'''
    serializer_class = ContactDetailSerializer
    permission_classes = [permissions.IsAuthenticated, IsCurrentUser, ]
    permission_classes_by_action = {
        'retrieve': [permissions.IsAuthenticated, ]
    }

    def get_queryset(self):
        pk = self.kwargs['pk']
        contact = get_object_or_404(Contact, id=pk)
        queryset = Contact.objects.filter(id=pk).annotate(
            is_friend=Count('friends', filter=Q(friends=self.request.user))
        ).annotate(
            num_friends=Count('friends')
        ).annotate(
            current_user=Count('slug', filter=Q(slug=self.request.user.slug))
        ).annotate(
            is_sent=Exists(
                AddRequest.objects.filter(
                    sender=self.request.user,
                    receiver=contact,
                )
            )
        ).annotate(
            is_sent_to_you=Exists(
                AddRequest.objects.filter(
                    sender=contact,
                    receiver=self.request.user
                )
            )
        ).annotate(
            chat_id=Sum('chats__id', filter=Q(chats__participants=self.request.user))
        )
        return queryset


В последней аннотации я пытаюсь вывести айди чата текущего пользователя с пользователем, на чью страницу он зашел, но возникает непредвиденная проблема

5f524389938a3965537335.png

Это я зашел на свой собственный контакт, под которым я залогинен, забавно здесь то, что друга у меня всего 3, а чата с айди 30 вообще не существует

Далее, если я привожу вью к следующему виду:
class ContactCustomViewSet(RetrieveUpdateDestroyPermissionViewset):
    '''Обзор, обновление и удаление контакта'''
    serializer_class = ContactDetailSerializer
    permission_classes = [permissions.IsAuthenticated, IsCurrentUser, ]
    permission_classes_by_action = {
        'retrieve': [permissions.IsAuthenticated, ]
    }

    def get_queryset(self):
        pk = self.kwargs['pk']
        contact = get_object_or_404(Contact, id=pk)
        queryset = Contact.objects.filter(id=pk).annotate(
            is_friend=Count('friends', filter=Q(friends=self.request.user))
        ).annotate(
            num_friends=Count('friends')
        ).annotate(
            current_user=Count('slug', filter=Q(slug=self.request.user.slug))
        ).annotate(
            is_sent=Exists(
                AddRequest.objects.filter(
                    sender=self.request.user,
                    receiver=contact,
                )
            )
        ).annotate(
            is_sent_to_you=Exists(
                AddRequest.objects.filter(
                    sender=contact,
                    receiver=self.request.user
                )
            )
        )
        # .annotate(
        #     chat_id=Sum('chats__id', filter=Q(chats__participants=self.request.user))
        # )
        return queryset


То результат следующий:

5f5243950e7df690887366.png

Друзья перестают шалить, что логично, но не понимаю я одного. Почему последняя аннотация вообще влияет на какую-либо другую, а также: как правильно все-таки построить запрос к базе для получения желаемого результата, а именно: как вывести айди чата с пользователем, на чью страницу я захожу. Мой фронт вытягивает айди чата из ответа, чтобы потом обратиться к правильному адресу с правильным айди чата, чтобы соответственно сделать соединение с правильным чатом

Буду очень благодарен за любую помощь
  • Вопрос задан
  • 121 просмотр
Решения вопроса 1
tumbler
@tumbler Куратор тега Django
бекенд-разработчик на python
  • Зря вы в аннотации идентификаторы суммируете
  • Чат для двух собеседников надо получать отдельно
    Chat.objects.filter(participants=request.user).filter(participants__id=friend_id_from_url)

  • Кроме того, в такой схеме данных возможны несколько чатов, в которых участвуют оба участника (и может еще кто-то посторонний). А еще сообщение может сразу нескольким чатам принадлежать
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы