-1👍
Everybody was right about the messages = serializers.SerializerMethodField()
. One thing that needs to be mentioned is ordering the list to be the last messages of the array.
For my implementation, I need the last 50 messages sorted in ascending timestamp (getting later) so this is what I did.
MESSAGE_COUNT = 50
class ChatSerializer(serializers.ModelSerializer):
messages = serializers.SerializerMethodField()
class Meta:
model = Deal
fields = '__all__'
def get_messages(self, chat):
qs = Message.objects.filter(deal=deal).order_by('timestamp')
if len(qs) > MESSAGE_COUNT:
qs = qs[len(qs)-MESSAGE_COUNT:]
return MessageSerializer(instance=qs, many=True).data
4👍
You can use a SerializerMethodField
within ChatSerializer
as follows:
messages = serializers.SerializerMethodField()
def get_messages(self, chat):
qs = Message.objects.filter(chat=chat).order_by('-date')[:50]
return MessageSerializer(instance=qs, many=True).data
This runs a separate query for every Chat
instance, but it only fetches the required number of rows. You’d have to customize the field names (chat
, date
) as applicable.
The alternative syntax @spiritsree is aiming at results in the same SQL, using implicit rather than explicit filtering:
qs = chat.messages.order_by('-date')[:50]
One thing to avoid is using prefetch_related('messages')
in the queryset
of the ViewSet
that returns the Chat
list as that prefetch won’t be used at all and would haul in all messages from the database only to be discarded unused.
The subquery alternative dismissed in another answer as slow is in fact quite interesting. It saves you as many roundtrips to the database as there are Chats. In exchange however, the database has to execute twice as many queries internally.
The excess queries being quite lightweight (selecting a small number of messages by id and ordering them), the saved roundtrips may easily make up for them. In my quick tests this method was more than 10x as fast as using SerializerMethodField
. It may depend to some extent on the data; test it for yourself:
from rest_framework import viewsets
from django.db.models import Prefetch, Subquery, OuterRef
class ChatViewSet(viewsets.ModelViewSet):
prefetch = Prefetch(
'messages',
queryset=Message.objects
.filter(id__in=Subquery(Message.objects
.filter(chat=OuterRef('chat_id'))
.order_by('-date')
.values_list('id', flat=True)[:4]))
.order_by('-date')
)
queryset = Chat.objects.prefetch_related(prefetch)
- [Django]-Django static files not loading
- [Django]-Displaying a message as a popup/alert in Django?
- [Django]-Cannot solve an AttributeError: 'Settings' object has no attribute 'ROOT_URLCONF' conundrum after doing a lot of research
- [Django]-From_db_value() takes 4 positional arguments but 5 were given Django
2👍
You could use SerializerMethodField to do that.
MESSAGE_COUNT = 50
class ChatSerializer(serializers.ModelSerializer):
messages = serializers.SerializerMethodField()
class Meta:
model = Chat
fields = '__all__'
def get_messages(self, obj):
return MessageSerializer(obj.messages[:MESSAGE_COUNT ], many=True).data
- [Django]-How to download data from azure-storage using get_blob_to_stream
- [Django]-Why I am Getting '_SIGCHLDWaker' object has no attribute 'doWrite' in Scrapy?
- [Django]-How do I create a user using OAuth2?
- [Django]-Django. PostgreSQL. regexp_split_to_table not working
0👍
The answer by @Tobey is unrelated to nested limiting and should be disregarded.
The answer by @spritsree would force the limiting to be performed in Python instead of at the Database level, where you want it to be done.
The details you are looking for are not implemented at the serializer level.
When you instantiate your serializer, you have to pass it the data that you wish to serialize. In your case, you should perform this reverse relationship limiting when gathering the data from the database.
I can only assume that you a 1-N relationship on Chat-Message. Therefore, you would WISH that you could do something like the following when writing your QuerySet:
QuerySet = Chat.objects.all().prefetch_related(
Prefetch(
"messages", queryset=Message.objects.all().order_by("-created_at")[:50]
)
)
However, Django does not support slicing in prefetch related query sets. There is a work around described, but you need to perform an IN query, and this is the slowest way of solving this issue.
Instead, you should separate the message gathering into a separate query:
# assuming that you are only interested in a single chat
chat = Chat.objects.latest("created_at")
messages = Message.objects.filter(chat=chat).order_by("created_at")[:50]
# instantiate serializer
serializer = ChatSerializer(data={"chat": chat, "messages": messages, ...})
serializer.data
...
- [Django]-Count the duplicates in list of dict in django python
- [Django]-How to check contents of incoming HTTP header request
- [Django]-Django Cannot Connect to SQL Server 2019