Corp/backend/api/v1/feed/serializers.py

200 lines
7.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# backend/api/v1/feed/serializers.py
from __future__ import annotations
from django.contrib.auth import get_user_model
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from feed.constants import TYPE_DEPARTMENT, TYPE_EMPLOYEE
from feed.models import Post, PostLike
from ..utils import build_media_url
Employee = get_user_model()
def _linked_task_payloads(post: Post, user) -> list[dict]:
if not user or not getattr(user, "is_authenticated", False):
return []
from tasks.models import TaskLinkedObjectKind
from tasks.presentation import linked_task_badges_for_object
return linked_task_badges_for_object(
obj=post,
kind=TaskLinkedObjectKind.POST,
user=user,
)
class AuthorMiniSerializer(serializers.ModelSerializer):
full_name = serializers.SerializerMethodField()
avatar = serializers.SerializerMethodField()
class Meta:
model = Employee
fields = ["id", "first_name", "last_name", "full_name", "avatar"]
@extend_schema_field(serializers.CharField())
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
@extend_schema_field(OpenApiTypes.URI)
def get_avatar(self, obj):
"""Возвращает полный URL для аватара"""
request = self.context.get("request")
return build_media_url(obj.avatar, request)
class PostLikerSerializer(AuthorMiniSerializer):
class Meta(AuthorMiniSerializer.Meta):
fields = ["id", "first_name", "last_name", "full_name", "avatar"]
class PostListSerializer(serializers.ModelSerializer):
author_id = serializers.IntegerField(read_only=True)
author = AuthorMiniSerializer(read_only=True)
department_name = serializers.CharField(
source="department.name",
read_only=True,
allow_null=True,
)
# удoбные представления дат
created_at_display = serializers.DateTimeField(
source="created_at", format="%d.%m.%Y %H:%M", read_only=True
)
pinned = serializers.SerializerMethodField()
pinned_global = serializers.BooleanField(read_only=True, default=False)
pinned_department = serializers.BooleanField(read_only=True, default=False)
# ожидания фронта
is_liked = serializers.BooleanField(read_only=True, default=False)
comments_count = serializers.IntegerField(
read_only=True, default=0, allow_null=True
)
# для ссылок на отдел
department_id = serializers.IntegerField(read_only=True)
# Переопределяем поля для полных URL
image = serializers.SerializerMethodField()
attachment = serializers.SerializerMethodField()
linked_tasks = serializers.SerializerMethodField()
class Meta:
model = Post
fields = [
"id",
"type",
"department",
"department_id",
"department_name",
"title",
"body",
"image",
"attachment",
"created_at",
"created_at_display",
"pinned",
"pinned_global",
"pinned_department",
"likes_count",
"comments_count",
"author_id",
"author",
"is_liked",
"linked_tasks",
]
read_only_fields = (
"created_at",
"created_at_display",
"pinned",
"pinned_global",
"pinned_department",
"likes_count",
"comments_count",
"author_id",
"author",
"department_id",
"department_name",
"is_liked",
"linked_tasks",
)
@extend_schema_field(OpenApiTypes.URI)
def get_image(self, obj):
"""Возвращает полный URL для изображения"""
request = self.context.get("request")
return build_media_url(obj.image, request)
@extend_schema_field(OpenApiTypes.URI)
def get_attachment(self, obj):
"""Возвращает полный URL для вложения"""
request = self.context.get("request")
return build_media_url(obj.attachment, request)
@extend_schema_field(serializers.BooleanField())
def get_pinned(self, obj):
scope = self.context.get("pin_scope") or "global"
if scope == "department":
return bool(getattr(obj, "pinned_department", False))
return bool(getattr(obj, "pinned_global", False))
def get_linked_tasks(self, obj) -> list[dict]:
request = self.context.get("request")
user = getattr(request, "user", None)
return _linked_task_payloads(obj, user)
class PostSerializer(PostListSerializer):
"""Сериализатор для деталей поста"""
body = serializers.CharField(required=False, allow_blank=True)
image = serializers.ImageField(required=False, allow_null=True, write_only=True)
attachment = serializers.FileField(required=False, allow_null=True, write_only=True)
user_has_liked = serializers.SerializerMethodField()
class Meta(PostListSerializer.Meta):
fields = PostListSerializer.Meta.fields + ["user_has_liked"]
@extend_schema_field(serializers.BooleanField())
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 to_representation(self, instance):
data = super().to_representation(instance)
request = self.context.get("request")
data["image"] = build_media_url(instance.image, request)
data["attachment"] = build_media_url(instance.attachment, request)
return data
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