193 lines
7.2 KiB
Python
Raw 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.

from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from tasks.models import (
TaskColumn,
TaskColumnRow,
TaskLabel,
)
from .common import validate_board_access
class TaskLabelSerializer(serializers.ModelSerializer):
class Meta:
model = TaskLabel
fields = ["id", "name", "color"]
read_only_fields = ["id"]
def validate_name(self, value):
name = value.strip()
duplicate = TaskLabel.objects.only("id", "name")
if self.instance:
duplicate = duplicate.exclude(pk=self.instance.pk)
normalized_name = name.casefold()
if any(label.name.casefold() == normalized_name for label in duplicate):
raise serializers.ValidationError("Метка с таким названием уже существует.")
return name
class TaskColumnSerializer(serializers.ModelSerializer):
tasks_count = serializers.IntegerField(read_only=True, default=0)
subcolumns_count = serializers.SerializerMethodField()
class Meta:
model = TaskColumn
validators = []
fields = [
"id",
"board",
"parent",
"name",
"position",
"color",
"is_done",
"is_archived",
"tasks_count",
"subcolumns_count",
"created_at",
"updated_at",
]
read_only_fields = [
"id",
"created_at",
"updated_at",
"tasks_count",
"subcolumns_count",
]
def validate_board(self, board):
return validate_board_access(self, board)
@extend_schema_field(serializers.IntegerField())
def get_subcolumns_count(self, obj):
annotated_count = getattr(obj, "subcolumns_count", None)
if annotated_count is not None:
return annotated_count
return obj.subcolumns.filter(is_archived=False).count()
def _validate_immutable_relations(self, attrs, board, parent) -> None:
if self.instance and board and self.instance.board_id != board.id:
raise serializers.ValidationError(
{"board": "Доску существующей колонки изменить нельзя."}
)
if self.instance and "parent" in attrs:
requested_parent_id = parent.id if parent else None
if self.instance.parent_id != requested_parent_id:
raise serializers.ValidationError(
{"parent": "Родителя существующей колонки изменить нельзя."}
)
@staticmethod
def _validate_parent(attrs, board, parent) -> None:
if not parent:
return
if board and parent.board_id != board.id:
raise serializers.ValidationError(
{"parent": "Подколонка должна принадлежать той же доске."}
)
if parent.parent_id:
raise serializers.ValidationError(
{"parent": "Допускается только один уровень подколонок."}
)
if parent.is_archived:
raise serializers.ValidationError(
{"parent": "Нельзя добавить подколонку в архивную колонку."}
)
attrs["is_done"] = parent.is_done
def _validate_archive(self, attrs) -> None:
archiving_parent = (
self.instance
and attrs.get("is_archived") is True
and self.instance.subcolumns.exists()
)
if archiving_parent:
raise serializers.ValidationError(
{"is_archived": "Сначала удалите подколонки."}
)
def _validate_unique_name(self, attrs, board, parent) -> None:
name = attrs.get("name", getattr(self.instance, "name", ""))
if not board or not name:
return
duplicate_columns = TaskColumn.objects.filter(name=name)
if parent:
duplicate_columns = duplicate_columns.filter(parent=parent)
else:
duplicate_columns = duplicate_columns.filter(
board=board,
parent__isnull=True,
)
if self.instance:
duplicate_columns = duplicate_columns.exclude(pk=self.instance.pk)
if duplicate_columns.exists():
raise serializers.ValidationError(
{"name": "Колонка с таким названием уже существует здесь."}
)
def validate(self, attrs):
board = attrs.get("board") or getattr(self.instance, "board", None)
parent = attrs.get("parent", getattr(self.instance, "parent", None))
self._validate_immutable_relations(attrs, board, parent)
self._validate_parent(attrs, board, parent)
self._validate_archive(attrs)
self._validate_unique_name(attrs, board, parent)
return attrs
def create(self, validated_data):
parent = validated_data.get("parent")
if parent:
validated_data.setdefault("color", parent.color)
validated_data.setdefault("is_done", parent.is_done)
return super().create(validated_data)
class TaskColumnRowSerializer(serializers.ModelSerializer):
tasks_count = serializers.IntegerField(read_only=True, default=0)
class Meta:
model = TaskColumnRow
fields = [
"id",
"column",
"name",
"position",
"color",
"tasks_count",
"created_at",
"updated_at",
]
read_only_fields = ["id", "tasks_count", "created_at", "updated_at"]
def validate_column(self, column):
validate_board_access(self, column.board)
if column.parent_id:
raise serializers.ValidationError(
"Дорожки можно создавать только в основной колонке."
)
if column.is_archived:
raise serializers.ValidationError(
"Нельзя создать дорожку в архивной колонке."
)
return column
def validate(self, attrs):
column = attrs.get("column") or getattr(self.instance, "column", None)
if self.instance and column and self.instance.column_id != column.id:
raise serializers.ValidationError(
{"column": "Основную колонку дорожки изменить нельзя."}
)
name = attrs.get("name", getattr(self.instance, "name", "")).strip()
if not name:
raise serializers.ValidationError({"name": "Укажите название дорожки."})
duplicate = TaskColumnRow.objects.filter(column=column, name__iexact=name)
if self.instance:
duplicate = duplicate.exclude(pk=self.instance.pk)
if duplicate.exists():
raise serializers.ValidationError(
{"name": "Дорожка с таким названием уже существует в колонке."}
)
attrs["name"] = name
return attrs