- Добавлено поле is_post_author в CommentSerializer - Сервер теперь явно указывает, является ли автор комментария автором поста - Фронтенд использует этот флаг (с fallback на ручное сравнение) - Добавлена детальная отладка для диагностики проблемы - Это устраняет проблему с неправильным определением автора
211 lines
7.1 KiB
Python
211 lines
7.1 KiB
Python
# backend/api/v1/feed/serializers.py
|
||
from __future__ import annotations
|
||
|
||
from typing import Any, Dict, Optional
|
||
|
||
from django.contrib.auth import get_user_model
|
||
from rest_framework import serializers
|
||
|
||
from feed.constants import TYPE_COMPANY, TYPE_DEPARTMENT, TYPE_EMPLOYEE
|
||
from feed.models import Comment, Post, PostLike
|
||
from ..serializers import Base64ImageField
|
||
|
||
Employee = get_user_model()
|
||
|
||
|
||
class AuthorMiniSerializer(serializers.ModelSerializer):
|
||
full_name = serializers.SerializerMethodField()
|
||
avatar = Base64ImageField(read_only=True)
|
||
|
||
class Meta:
|
||
model = Employee
|
||
fields = ["id", "first_name", "last_name", "full_name", "avatar"]
|
||
|
||
def get_full_name(self, obj):
|
||
fn = (obj.first_name or "").strip()
|
||
ln = (obj.last_name or "").strip()
|
||
nm = f"{fn} {ln}".strip()
|
||
return nm
|
||
|
||
|
||
class CommentMiniSerializer(serializers.ModelSerializer):
|
||
author = AuthorMiniSerializer(read_only=True)
|
||
created_at_display = serializers.DateTimeField(
|
||
source="created_at", format="%d.%m.%Y %H:%M", read_only=True
|
||
)
|
||
|
||
class Meta:
|
||
model = Comment
|
||
fields = ["id", "text", "created_at", "created_at_display", "author"]
|
||
|
||
|
||
class PostListSerializer(serializers.ModelSerializer):
|
||
author_id = serializers.IntegerField(read_only=True)
|
||
author = AuthorMiniSerializer(read_only=True)
|
||
|
||
# удoбные представления дат
|
||
created_at_display = serializers.DateTimeField(
|
||
source="created_at", format="%d.%m.%Y %H:%M", read_only=True
|
||
)
|
||
|
||
# ожидания фронта
|
||
is_liked = serializers.BooleanField(read_only=True, default=False)
|
||
comments_count = serializers.IntegerField(read_only=True, default=0)
|
||
# для ссылок на отдел
|
||
department_id = serializers.IntegerField(read_only=True)
|
||
last_comment = serializers.SerializerMethodField()
|
||
|
||
class Meta:
|
||
model = Post
|
||
fields = [
|
||
"id",
|
||
"type",
|
||
"department",
|
||
"department_id",
|
||
"title",
|
||
"body",
|
||
"image",
|
||
"attachment",
|
||
"created_at",
|
||
"created_at_display",
|
||
"pinned",
|
||
"likes_count",
|
||
"comments_count",
|
||
"author_id",
|
||
"author",
|
||
"is_liked",
|
||
"last_comment",
|
||
]
|
||
read_only_fields = (
|
||
"created_at",
|
||
"created_at_display",
|
||
"pinned",
|
||
"likes_count",
|
||
"comments_count",
|
||
"author_id",
|
||
"author",
|
||
"department_id",
|
||
"is_liked",
|
||
)
|
||
|
||
def get_last_comment(self, obj):
|
||
# Берём готовую мапу из контекста, собранную во viewset.list()
|
||
lc_map = self.context.get("last_comments_map") or {}
|
||
lc = None
|
||
lc_id = getattr(obj, "last_comment_id", None)
|
||
if lc_id:
|
||
lc = lc_map.get(lc_id)
|
||
if not lc:
|
||
return None
|
||
return CommentMiniSerializer(lc, context=self.context).data
|
||
|
||
|
||
class PostSerializer(PostListSerializer):
|
||
"""Сериализатор для деталей поста с комментариями"""
|
||
comments = serializers.SerializerMethodField()
|
||
user_has_liked = serializers.SerializerMethodField()
|
||
|
||
class Meta(PostListSerializer.Meta):
|
||
fields = PostListSerializer.Meta.fields + [
|
||
"comments",
|
||
"user_has_liked"
|
||
]
|
||
|
||
def get_comments(self, obj):
|
||
"""Возвращает список комментариев к посту"""
|
||
comments = obj.comments.select_related("author").order_by(
|
||
"created_at"
|
||
)
|
||
return CommentSerializer(
|
||
comments, many=True, context=self.context
|
||
).data
|
||
|
||
def get_user_has_liked(self, obj):
|
||
"""Проверяет, лайкнул ли текущий пользователь этот пост"""
|
||
request = self.context.get("request")
|
||
if request and request.user and request.user.is_authenticated:
|
||
return PostLike.objects.filter(
|
||
post=obj, user=request.user
|
||
).exists()
|
||
return False
|
||
|
||
def validate(self, attrs):
|
||
# значения с учётом partial
|
||
t = attrs.get("type", getattr(self.instance, "type", None))
|
||
dept = attrs.get("department", getattr(self.instance, "department", None))
|
||
|
||
# личные посты запрещены
|
||
if t == TYPE_EMPLOYEE:
|
||
raise serializers.ValidationError(
|
||
{"type": "Публикация на личной странице запрещена."}
|
||
)
|
||
|
||
# связка type/department
|
||
if t == TYPE_DEPARTMENT and not dept:
|
||
raise serializers.ValidationError(
|
||
{"department": "Нужно указать отдел для новостей отдела."}
|
||
)
|
||
if t != TYPE_DEPARTMENT and dept is not None:
|
||
raise serializers.ValidationError(
|
||
{"department": "Для этого типа публикаций отдел указывать нельзя."}
|
||
)
|
||
return attrs
|
||
|
||
|
||
class CommentSerializer(serializers.ModelSerializer):
|
||
post_id = serializers.IntegerField(read_only=True)
|
||
author_id = serializers.IntegerField(read_only=True)
|
||
author = AuthorMiniSerializer(read_only=True)
|
||
image = serializers.FileField(allow_null=True, required=False)
|
||
attachment = serializers.FileField(allow_null=True, required=False)
|
||
created_at_display = serializers.DateTimeField(
|
||
source="created_at", format="%d.%m.%Y %H:%M", read_only=True
|
||
)
|
||
is_post_author = serializers.SerializerMethodField()
|
||
|
||
class Meta:
|
||
model = Comment
|
||
fields = [
|
||
"id",
|
||
"post",
|
||
"post_id",
|
||
"author_id",
|
||
"author",
|
||
"text",
|
||
"image",
|
||
"attachment",
|
||
"created_at",
|
||
"created_at_display",
|
||
"is_post_author",
|
||
]
|
||
extra_kwargs = {
|
||
"post": {"write_only": True},
|
||
"text": {"required": False, "allow_blank": True},
|
||
}
|
||
|
||
def get_is_post_author(self, obj):
|
||
"""Проверяет, является ли автор комментария автором поста"""
|
||
if not obj.author or not obj.post or not obj.post.author:
|
||
return False
|
||
return obj.author.id == obj.post.author.id
|
||
|
||
def validate(self, attrs):
|
||
text = (attrs.get("text") or "").strip()
|
||
if not (text or attrs.get("image") or attrs.get("attachment")):
|
||
raise serializers.ValidationError(
|
||
{"text": "Нужно указать текст, изображение или файл."}
|
||
)
|
||
return attrs
|
||
|
||
def create(self, validated_data):
|
||
# author выставляется во view
|
||
instance = Comment(**validated_data)
|
||
instance.save(skip_validation=True)
|
||
return instance
|
||
|
||
def update(self, instance, validated_data):
|
||
for k, v in validated_data.items():
|
||
setattr(instance, k, v)
|
||
instance.save(skip_validation=True)
|
||
return instance
|