#django #graphql
Вопрос:
У меня есть API Django GraphQL (с использованием графена), который я использую для создания приложения для чата. Приложение позволяет общаться как один на один, так и в групповом чате, а в пользовательском интерфейсе (на основе углового) есть одна строка поиска. Поэтому, когда пользователь вводит поисковый запрос, я хотел бы показать им все следующее в одном списке:-
- Участники, имя или фамилия которых содержат запрос
- Групповые чаты, в названии которых содержится запрос
- Отдельные сообщения чата, содержащие запрос
Вот модели:-
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. Я не уверен, что такая нестандартная форма может быть достигнута. Пожалуйста, дайте мне знать, какова наилучшая стратегия.