Как я могу заставить запрос GraphQL отвечать массивом из нескольких типов объектов? Я использую Django GraphQL API с использованием графена

#django #graphql

Вопрос:

У меня есть API Django GraphQL (с использованием графена), который я использую для создания приложения для чата. Приложение позволяет общаться как один на один, так и в групповом чате, а в пользовательском интерфейсе (на основе углового) есть одна строка поиска. Поэтому, когда пользователь вводит поисковый запрос, я хотел бы показать им все следующее в одном списке:-

  1. Участники, имя или фамилия которых содержат запрос
  2. Групповые чаты, в названии которых содержится запрос
  3. Отдельные сообщения чата, содержащие запрос

Вот модели:-

 
class Group(models.Model):
    name = models.CharField(max_length=50)
    description = models.CharField(max_length=250)
    institution = models.ForeignKey(Institution, on_delete=models.PROTECT)
    avatar = models.CharField(
        max_length=250, blank=True, null=True, default="https://i.imgur.com/hNdMk4c.png")
    searchField = models.CharField(max_length=400, blank=True, null=True)

    # Type Choices
    class TypeChoices(models.TextChoices):
        CLASS = "CL", _('Class')
        TEAM = "TE", _('Team')
        COORDINATiON = "CO", _('Coordination')
    # End of Type Choices

    group_type = models.CharField(
        max_length=2, choices=TypeChoices.choices, default=TypeChoices.TEAM)
    admins = models.ManyToManyField(User, related_name="adminInGroups", through="GroupAdmin", through_fields=(
        'group', 'admin'), blank=True)
    members = models.ManyToManyField(
        User, related_name="memberInGroups", through='GroupMember', through_fields=('group', 'member'), blank=True)
    active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name


class GroupAdmin(models.Model):
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    admin = models.ForeignKey(User, on_delete=models.CASCADE)

    def __str__(self):
        return self.group


class GroupMember(models.Model):
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    member = models.ForeignKey(User, on_delete=models.CASCADE)

    def __str__(self):
        return self.group


class Chat(models.Model):
    group = models.OneToOneField(
        Group, on_delete=models.CASCADE, blank=True, null=True)
    individual_member_one = models.ForeignKey(
        User, related_name="chat_member_one", on_delete=models.CASCADE, blank=True, null=True)
    individual_member_two = models.ForeignKey(
        User, related_name="chat_member_two", on_delete=models.CASCADE, blank=True, null=True)
    # Type Choices

    class TypeChoices(models.TextChoices):
        INDIVIDUAL = "IL", _('Individual')
        GROUP = "GP", _('Group')
    # End of Type Choices

    chat_type = models.CharField(
        max_length=2, choices=TypeChoices.choices, default=TypeChoices.INDIVIDUAL)

    active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name




class ChatMessage(models.Model):
    chat = models.ForeignKey(Chat, on_delete=models.PROTECT)
    message = models.CharField(max_length=1000)
    author = models.ForeignKey(
        User, related_name="chatAuthor", on_delete=models.DO_NOTHING)
    seenBy = models.ForeignKey(
        User, related_name="chatSeenBy", on_delete=models.PROTECT, blank=True, null=True)
    active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.chat

 

Мой текущий запрос выбирает только отдельных пользователей, у которых есть запрос в их searchField атрибуте (он автоматически заполняется именем фамилией в одной строке во время создания/обновления).

 class UserType(DjangoObjectType):
    total_count = graphene.Int()
    
    def resolve_total_count(self, info):
        count = User.objects.all().filter(active=True).count()
        return count
    
    class Meta:
        model = User
    
chat_search = graphene.List(
        UserType, query=graphene.String())
 
 
    @login_required
    def resolve_chat_search(root, info, query=None, **kwargs):
        current_user = info.context.user


        if query is not None:
            filter = (
                Q(searchField__icontains=query)
            )
            qs = qs.filter(filter)
            qs = qs.exclude()

        return qs
 

I want to expand this query to the following:-

     @login_required
    def resolve_chat_search(root, info, query=None, **kwargs):
        current_user = info.context.user

        # "~Q(id=user_id)" is meant to exclude the current user from the results
        qs = User.objects.all().filter(~Q(id=current_user.id), active=True).order_by('-id')
        groups = Group.objects.all().filter(
            Q(members__in=[current_user]) | Q(admins__in=[current_user]))

        chat_gp = Chat.objects.all().filter(active=True, chat_type='GP', members__in=[
            current_user.id]).order_by('-id')

        chat_il = Chat.objects.all().filter(active=True, chat_type='IL')
        chat_il = chat_il.filter(Q(individual_member_one=current_user.id) | Q(
            individual_member_two=current_user.id))

        chats = chat_gp | chat_il
        chat_messages = ChatMessage.objects.all().filter(chat__in=[chats])
        qs = qs | groups | chat_messages

        if query is not None:
            filter = (
                Q(searchField__icontains=query)
            )
            qs = qs.filter(filter)
            qs = qs.exclude()

        return qs
 

This above code fetches all the relevant members, groups and chat messages into a single list, but obviously this is going to throw an error because the chat_search query is supposed to return a response of user_type . This one is a mix of User, Chat and ChatMessage.

I could technically make 3 separate calls (to search for the three types separately) and stitch the responses together in the UI to achieve the functionality. But that would go against the whole point of using something like GraphQL which is supposed to minimize the number of calls I make.

На самом деле я бы предпочел, чтобы ответ был следующего типа:-

 data {
    users: <MatchingUsers>,
    chats: <MatchingGroupChats>,
    messages: <MatchingChatMessages>
}
 

Я довольно новичок в серверной части GraphQL. Я не уверен, что такая нестандартная форма может быть достигнута. Пожалуйста, дайте мне знать, какова наилучшая стратегия.