@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

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

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

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

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

Войти через центр авторизации
Похожие вопросы
Junto Москва
от 80 000 до 120 000 ₽
Maroom Москва
от 100 000 до 160 000 ₽
Верфь Екатеринбург
от 70 000 до 140 000 ₽
28 сент. 2020, в 00:10
15000 руб./за проект
27 сент. 2020, в 21:54
15000 руб./за проект