from rest_framework import serializers from django.utils import timezone from .models import Code, Page, CraftNode, NodeType # (네 models.py에 맞춰 import) # ------------------------- # Node (CraftJS) # ------------------------- class NodeTypeSerializer(serializers.Serializer): resolvedName = serializers.CharField() class CraftNodeSerializer(serializers.Serializer): type = NodeTypeSerializer() isCanvas = serializers.BooleanField(required=False, default=False) props = serializers.DictField(required=False, default=dict) displayName = serializers.CharField(required=False, allow_blank=True, allow_null=True) custom = serializers.DictField(required=False, default=dict) hidden = serializers.BooleanField(required=False, default=False) nodes = serializers.ListField(child=serializers.CharField(), required=False, default=list) linkedNodes = serializers.DictField(required=False, default=dict) parent = serializers.CharField(required=False, allow_null=True, allow_blank=True) # ------------------------- # Page # ------------------------- class PageSerializer(serializers.Serializer): page_id = serializers.IntegerField() title = serializers.CharField() # data: { "ROOT": {...}, "mBIaZ3L5VU": {...}, ... } # -> key는 문자열(node id), value는 CraftNode data = serializers.DictField( child=CraftNodeSerializer(), required=False, default=dict, ) # ------------------------- # Code # ------------------------- class CodeSerializer(serializers.Serializer): id = serializers.SerializerMethodField() keyword = serializers.ListField( child=serializers.CharField(max_length=50), required=False, allow_empty=True, default=list, ) description = serializers.CharField(required=False, allow_blank=True, default="") pages = PageSerializer(many=True, required=False, default=list) created_at = serializers.SerializerMethodField() updated_at = serializers.SerializerMethodField() def get_id(self, obj): return str(obj.id) def get_created_at(self, obj): return timezone.localtime(obj.created_at).isoformat(timespec="seconds") def get_updated_at(self, obj): return timezone.localtime(obj.updated_at).isoformat(timespec="seconds") # ------------------------- # helpers: dict -> EmbeddedDocument # ------------------------- def _to_node_type(self, data: dict) -> NodeType: return NodeType(resolvedName=data.get("resolvedName", "")) def _to_craft_node(self, data: dict) -> CraftNode: # type 필수 node_type = self._to_node_type(data["type"]) return CraftNode( type=node_type, isCanvas=data.get("isCanvas", False), props=data.get("props", {}) or {}, displayName=data.get("displayName"), custom=data.get("custom", {}) or {}, hidden=data.get("hidden", False), nodes=data.get("nodes", []) or [], linkedNodes=data.get("linkedNodes", {}) or {}, parent=data.get("parent"), ) def _to_page(self, page: dict) -> Page: raw_map = page.get("data", {}) or {} node_map = {node_id: self._to_craft_node(node_dict) for node_id, node_dict in raw_map.items()} return Page( page_id=page["page_id"], title=page["title"], data=node_map, ) # ------------------------- # create / update # ------------------------- def create(self, validated_data): request = self.context["request"] validated_data["object_type"] = request.query_params.get("type") validated_data["object_id"] = request.query_params.get("id") pages_data = validated_data.pop("pages", []) pages = [self._to_page(p) for p in pages_data] code = Code(pages=pages, **validated_data) code.save() return code def update(self, instance, validated_data): # pages는 page_id 기준으로 upsert update_pages_data = validated_data.get("pages", []) existing = {p.page_id: p for p in instance.pages} for page in update_pages_data: pid = page.get("page_id") if pid is None: continue new_page_obj = self._to_page(page) if pid in existing: page_obj = existing[pid] page_obj.title = new_page_obj.title page_obj.data = new_page_obj.data else: instance.pages.append(new_page_obj) if "keyword" in validated_data: instance.keyword = validated_data["keyword"] if "description" in validated_data: instance.description = validated_data["description"] instance.save() return instance