I am building a Django GraphQL API to use for a chat app. I have the following 2 models, one for Chat and one for chat members, which is tied in as a foreingKey:-
class Chat(models.Model):
name = models.CharField(max_length=100, blank=True, null=True)
members = models.ManyToManyField(User, related_name="privateChats",
through="ChatMember", through_fields=('chat', '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 ChatMember(models.Model):
chat = models.ForeignKey(Chat, on_delete=models.CASCADE)
member = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.chat
I have a certain method that is supposed to take in 2 user Ids and first it looks to check if an existing chat exists between these 2 members. If it exists, it returns that chat. If not then it creates a new chat for these 2 members and returns the newly created chat.
The tricky part is where it tries to search if an existing chat exists. Because I'm using this same chat model for group chats, it is possible that it could return a group that also has these 2 members. So the condition to check if there is a one on one chat between these 2 members, it is to first check if the chat has only 2 members in it and those 2 members have the ids that I get as the input for that method.
I'm very new to Django and Python, so I don't know to write the query to find if such a chat exists. So far this is what I have done for that:-
class ChatWithMember(graphene.Mutation):
class Meta:
description = "Mutation to get into a Chat"
class Arguments:
# ***This is the first user's Id.***
id = graphene.ID(required=True)
ok = graphene.Boolean()
chat = graphene.Field(ChatType)
@staticmethod
@login_required
def mutate(root, info, id):
ok = True
# ***Second user's id is taken from the request***
current_user = info.context.user
member = User.objects.get(pk=id)
if member is None:
return ChatWithMember(ok=False, chat=None)
# ***First I annotate the number of members in 'individual_chats'***
individual_chats = Chat.objects.annotate(member_count=Count('members'))
# ***Then I filter those chats that have only 2 members***
individual_chats = individual_chats.filter(member_count=2)
# ***Then I loop through those to check if there's one that has the two members in that chat and if it exists, I return it.***
for chat in individual_chats.all():
if current_user.id in chat.members and member.id in chat.members:
return ChatWithMember(ok=ok, chat=chat)
chat_instance = Chat()
chat_instance.save()
# Adding the creator of the chat and the member
chat_instance.members.set([current_user.id, id])
return ChatWithMember(ok=ok, chat=chat_instance)
At the moment I get the error __str__ returned non-string (type NoneType)
, previously I got TypeError: ManyRelatedManager object is not iterable
. I've spent quite a bit of time on this and clearly I could use some help.
I've written this method with my own understanding of how to get it done, but I'm not sure if a simpler approach exists. Please let me know how to go about it.
Update:-
I've modified the method as per suggestions from @Henri:-
def mutate(root, info, id):
ok = True
current_user = info.context.user
member = User.objects.get(pk=id)
if member is None:
return ChatWithMember(ok=False, chat=None)
individual_chats = Chat.objects.annotate(member_count=Count('members'))
individual_chats = individual_chats.filter(member_count=2)
print('Individual chats before try => ',
individual_chats.values_list('name', 'members__id'))
try:
chats_with_these_2_members = individual_chats.get(
members__in=[member, current_user])
return ChatWithMember(ok=ok, chat=chats_with_these_2_members)
except MultipleObjectsReturned:
print('multiple chats found!')
return ChatWithMember(ok=False, chat=None)
# Error because multiple chats found with these 2 members (and only them)
except Chat.DoesNotExist:
print('Creating new chat')
chat_instance = Chat()
chat_instance.save()
# Adding the creator of the chat and the member
chat_instance.members.set([current_user.id, id])
return ChatWithMember(ok=ok, chat=chat_instance)
With this in place, when I try to trigger this method with two users who already have a chat with them as the only 2 members, I get the following output in the console:-
Individual chats before try => <QuerySet []> Creating new chat
So basically it fails to identify the existence of the chat that qualifies and goes ahead and creates a new chat.
What's worse is if I try to initiate a chat with different member combination after the first chat is created, I get this:-
Individual chats before try => <QuerySet []> multiple chats found!
This means it no longer creates new chats for different member combinations.
There's something very wrong with this part:-
try:
chats_with_these_2_members = individual_chats.get(
members__in=[member, current_user])
return ChatWithMember(ok=ok, chat=chats_with_these_2_members)
except MultipleObjectsReturned:
print('multiple chats found!')
return ChatWithMember(ok=False, chat=None)
# Error because multiple chats found with these 2 members (and only them)
except Chat.DoesNotExist:
but I'm not sure how to fix it because I'm so new t Python.