feat: ADRF async views (phase1) + native async serializers (phase2) + async cache infra
This commit is contained in:
+51
-44
@@ -1,18 +1,20 @@
|
||||
from rest_framework import serializers
|
||||
from adrf.serializers import (
|
||||
ModelSerializer, Serializer, IntegerField, CharField, SerializerMethodField,
|
||||
)
|
||||
from .models import FriendRequest, Friendship, Conversation, ConversationParticipant, ChatMessage
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class UserBriefSerializer(serializers.Serializer):
|
||||
id = serializers.IntegerField()
|
||||
username = serializers.CharField()
|
||||
nickname = serializers.SerializerMethodField()
|
||||
avatar = serializers.SerializerMethodField()
|
||||
class UserBriefSerializer(Serializer):
|
||||
id = IntegerField()
|
||||
username = CharField()
|
||||
nickname = SerializerMethodField()
|
||||
avatar = SerializerMethodField()
|
||||
|
||||
def get_nickname(self, obj):
|
||||
async def get_nickname(self, obj):
|
||||
return getattr(obj, 'nickname', '') or obj.username
|
||||
|
||||
def get_avatar(self, obj):
|
||||
async def get_avatar(self, obj):
|
||||
avatar = getattr(obj, 'avatar', None)
|
||||
if avatar and hasattr(avatar, 'url'):
|
||||
request = self.context.get('request')
|
||||
@@ -22,91 +24,96 @@ class UserBriefSerializer(serializers.Serializer):
|
||||
return ''
|
||||
|
||||
|
||||
class FriendRequestSerializer(serializers.ModelSerializer):
|
||||
class FriendRequestSerializer(ModelSerializer):
|
||||
from_user = UserBriefSerializer(read_only=True)
|
||||
to_user = UserBriefSerializer(read_only=True)
|
||||
from_user_id = serializers.IntegerField(write_only=True, required=False)
|
||||
to_user_id = serializers.IntegerField(write_only=True, required=False)
|
||||
from_user_id = IntegerField(write_only=True, required=False)
|
||||
to_user_id = IntegerField(write_only=True, required=False)
|
||||
|
||||
class Meta:
|
||||
model = FriendRequest
|
||||
fields = ['id', 'from_user', 'to_user', 'from_user_id', 'to_user_id', 'status', 'message', 'created_at', 'updated_at']
|
||||
|
||||
|
||||
class FriendshipSerializer(serializers.ModelSerializer):
|
||||
friend = serializers.SerializerMethodField()
|
||||
class FriendshipSerializer(ModelSerializer):
|
||||
friend = SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Friendship
|
||||
fields = ['id', 'friend', 'created_at']
|
||||
|
||||
def get_friend(self, obj):
|
||||
async def get_friend(self, obj):
|
||||
request = self.context.get('request')
|
||||
if not request:
|
||||
return None
|
||||
friend_user = obj.user2 if obj.user1 == request.user else obj.user1
|
||||
return UserBriefSerializer(friend_user, context=self.context).data
|
||||
friend_user = obj.user2 if obj.user1_id == request.user.id else obj.user1
|
||||
return await UserBriefSerializer(friend_user, context=self.context).adata
|
||||
|
||||
|
||||
class ConversationSerializer(serializers.ModelSerializer):
|
||||
other_user = serializers.SerializerMethodField()
|
||||
last_message = serializers.SerializerMethodField()
|
||||
unread_count = serializers.SerializerMethodField()
|
||||
class ConversationSerializer(ModelSerializer):
|
||||
other_user = SerializerMethodField()
|
||||
last_message = SerializerMethodField()
|
||||
unread_count = SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Conversation
|
||||
fields = ['id', 'type', 'other_user', 'last_message', 'unread_count', 'created_at']
|
||||
|
||||
def get_other_user(self, obj):
|
||||
async def get_other_user(self, obj):
|
||||
request = self.context.get('request')
|
||||
if not request:
|
||||
return None
|
||||
participant = obj.participants.exclude(user=request.user).first()
|
||||
participant = await obj.participants.exclude(user=request.user).afirst()
|
||||
if participant:
|
||||
return UserBriefSerializer(participant.user, context=self.context).data
|
||||
return await UserBriefSerializer(participant.user, context=self.context).adata
|
||||
return None
|
||||
|
||||
def get_last_message(self, obj):
|
||||
last_msg = obj.messages.order_by('-created_at').first()
|
||||
async def get_last_message(self, obj):
|
||||
last_msg = await obj.messages.order_by('-created_at').select_related('sender', 'reply_to__sender').afirst()
|
||||
if last_msg:
|
||||
return ChatMessageSerializer(last_msg, context=self.context).data
|
||||
return await ChatMessageSerializer(last_msg, context=self.context).adata
|
||||
return None
|
||||
|
||||
def get_unread_count(self, obj):
|
||||
async def get_unread_count(self, obj):
|
||||
request = self.context.get('request')
|
||||
if not request:
|
||||
return 0
|
||||
try:
|
||||
participant = ConversationParticipant.objects.get(conversation=obj, user=request.user)
|
||||
participant = await ConversationParticipant.objects.aget(conversation=obj, user=request.user)
|
||||
if participant.last_read_at:
|
||||
return obj.messages.filter(created_at__gt=participant.last_read_at).exclude(sender=request.user).count()
|
||||
return obj.messages.exclude(sender=request.user).count()
|
||||
return await obj.messages.filter(created_at__gt=participant.last_read_at).exclude(sender=request.user).acount()
|
||||
return await obj.messages.exclude(sender=request.user).acount()
|
||||
except ConversationParticipant.DoesNotExist:
|
||||
return 0
|
||||
|
||||
|
||||
class ChatMessageSerializer(serializers.ModelSerializer):
|
||||
class ChatMessageSerializer(ModelSerializer):
|
||||
sender_info = UserBriefSerializer(source='sender', read_only=True)
|
||||
is_own = serializers.SerializerMethodField()
|
||||
reply_to_message = serializers.SerializerMethodField()
|
||||
is_own = SerializerMethodField()
|
||||
reply_to_message = SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = ChatMessage
|
||||
fields = ['id', 'conversation', 'sender', 'sender_info', 'content', 'msg_type', 'file_url', 'reply_to', 'reply_to_message', 'is_recalled', 'is_own', 'created_at']
|
||||
read_only_fields = ['sender', 'conversation']
|
||||
|
||||
def get_is_own(self, obj):
|
||||
async def get_is_own(self, obj):
|
||||
request = self.context.get('request')
|
||||
if request and hasattr(request, 'user'):
|
||||
return obj.sender == request.user
|
||||
# 用 id 比较,避免 sender 外键懒加载触发同步查询
|
||||
return obj.sender_id == request.user.id
|
||||
return False
|
||||
|
||||
def get_reply_to_message(self, obj):
|
||||
if obj.reply_to and not obj.reply_to.is_recalled:
|
||||
return {
|
||||
'id': obj.reply_to.id,
|
||||
'content': obj.reply_to.content[:50],
|
||||
'sender_name': getattr(obj.reply_to.sender, 'nickname', '') or obj.reply_to.sender.username,
|
||||
'msg_type': obj.reply_to.msg_type,
|
||||
}
|
||||
return None
|
||||
async def get_reply_to_message(self, obj):
|
||||
if not obj.reply_to_id:
|
||||
return None
|
||||
# 异步加载被回复消息及其发送者,避免外键懒加载触发同步查询
|
||||
reply = await ChatMessage.objects.select_related('sender').aget(pk=obj.reply_to_id)
|
||||
if reply.is_recalled:
|
||||
return None
|
||||
return {
|
||||
'id': reply.id,
|
||||
'content': reply.content[:50],
|
||||
'sender_name': getattr(reply.sender, 'nickname', '') or reply.sender.username,
|
||||
'msg_type': reply.msg_type,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user