from django.contrib.auth import get_user_model from rest_framework import serializers from drf_spectacular.utils import extend_schema_field from .models import Chat, ChatReadStatus, Message, MessageFile, MessageHistory, MessageReaction class MessageSenderSerializer(serializers.ModelSerializer): avatar = serializers.SerializerMethodField() def get_avatar(self, obj): from django.conf import settings if obj.avatar: return settings.MEDIA_URL + obj.avatar.name return None class Meta: model = get_user_model() fields = ['id', 'username', 'avatar'] read_only_fields = ['id', 'username'] class MessageFileSerializer(serializers.ModelSerializer): class Meta: model = MessageFile fields = ['id', 'file', 'media_type', 'uploaded_at'] read_only_fields = ['uploaded_at'] class MessageReactionSerializer(serializers.ModelSerializer): class Meta: model = MessageReaction fields = ['id', 'user', 'emoji', 'created_at'] read_only_fields = ['user', 'created_at'] class MessageHistorySerializer(serializers.ModelSerializer): class Meta: model = MessageHistory fields = ['id', 'old_content', 'archived_at'] read_only_fields = ['archived_at'] class ReplyToSerializer(serializers.ModelSerializer): sender = MessageSenderSerializer(read_only=True) class Meta: model = Message fields = ['id', 'content', 'sender'] read_only_fields = ['id', 'content', 'sender'] class MessageSerializer(serializers.ModelSerializer): sender = MessageSenderSerializer(read_only=True) reply_to = ReplyToSerializer(read_only=True) media_files = MessageFileSerializer(many=True, read_only=True) reactions = MessageReactionSerializer(many=True, read_only=True) class Meta: model = Message fields = [ 'id', 'chat', 'sender', 'reply_to', 'content', 'is_edited', 'edited_at', 'created_at', 'updated_at', 'media_files', 'reactions', ] read_only_fields = ['sender', 'chat', 'reply_to', 'is_edited', 'edited_at', 'created_at', 'updated_at'] class MessageSendSerializer(serializers.Serializer): """Used for the HTTP send endpoint (text + optional files).""" chat = serializers.PrimaryKeyRelatedField(queryset=Chat.objects.all()) content = serializers.CharField(required=False, allow_blank=True, default='') reply_to = serializers.PrimaryKeyRelatedField( queryset=Message.objects.all(), required=False, allow_null=True ) # files come via request.FILES - declared here for schema documentation only files = serializers.ListField( child=serializers.FileField(allow_empty_file=False, use_url=False), required=False, default=list, write_only=True, ) def validate(self, attrs): if not attrs.get('content') and not attrs.get('files'): raise serializers.ValidationError('A message must have content or at least one file.') return attrs class ChatSerializer(serializers.ModelSerializer): unread_count = serializers.SerializerMethodField(read_only=True) members_detail = MessageSenderSerializer(source='members', many=True, read_only=True) @extend_schema_field(serializers.IntegerField()) def get_unread_count(self, obj): request = self.context.get('request') if not request or not request.user.is_authenticated: return 0 last_read = ChatReadStatus.objects.filter( user=request.user, chat=obj ).values_list('last_read_at', flat=True).first() if last_read is None: return obj.messages.filter(is_deleted=False).count() return obj.messages.filter(created_at__gt=last_read, is_deleted=False).count() class Meta: model = Chat fields = [ 'id', 'chat_type', 'owner', 'name', 'icon', 'banner', 'members', 'moderators', 'hub', 'created_at', 'updated_at', 'unread_count', 'members_detail', ] read_only_fields = ['owner', 'created_at', 'updated_at', 'unread_count', 'members_detail'] class ChatMemberSerializer(serializers.Serializer): user_id = serializers.IntegerField(help_text='PK of the user to add or remove.')