66from openedx_learning .apps .authoring .components import api as components_api
77
88
9- class ComponentSerializer (serializers .Serializer ): # pylint: disable=abstract-method
9+ class EntitySerializer (serializers .Serializer ): # pylint: disable=abstract-method
1010 """
11- Serializer for components.
12- Contains logic to convert entity_key to component_type and local_key.
11+ Serializer for publishable entities.
1312 """
1413 can_stand_alone = serializers .BooleanField (required = True )
1514 key = serializers .CharField (required = True )
1615 created = serializers .DateTimeField (required = True )
1716 created_by = serializers .CharField (required = True , allow_null = True )
1817
18+
19+ class EntityVersionSerializer (serializers .Serializer ): # pylint: disable=abstract-method
20+ """
21+ Serializer for publishable entity versions.
22+ """
23+ title = serializers .CharField (required = True )
24+ entity_key = serializers .CharField (required = True )
25+ created = serializers .DateTimeField (required = True )
26+ created_by = serializers .CharField (required = True , allow_null = True )
27+
28+
29+ class ComponentSerializer (EntitySerializer ): # pylint: disable=abstract-method
30+ """
31+ Serializer for components.
32+ Contains logic to convert entity_key to component_type and local_key.
33+ """
34+
1935 def validate (self , attrs ):
2036 """
2137 Custom validation logic:
@@ -31,12 +47,76 @@ def validate(self, attrs):
3147 return attrs
3248
3349
34- class ComponentVersionSerializer (serializers . Serializer ): # pylint: disable=abstract-method
50+ class ComponentVersionSerializer (EntityVersionSerializer ): # pylint: disable=abstract-method
3551 """
3652 Serializer for component versions.
3753 """
38- title = serializers .CharField (required = True )
39- entity_key = serializers .CharField (required = True )
40- created = serializers .DateTimeField (required = True )
41- created_by = serializers .CharField (required = True , allow_null = True )
4254 content_to_replace = serializers .DictField (child = serializers .CharField (), required = True )
55+
56+
57+ class ContainerSerializer (EntitySerializer ): # pylint: disable=abstract-method
58+ """
59+ Serializer for containers.
60+ """
61+ container = serializers .DictField (required = True )
62+
63+ def validate_container (self , value ):
64+ """
65+ Custom validation logic for the container field.
66+ Ensures that the container dict has exactly one key which is one of
67+ "section", "subsection", or "unit" values.
68+ """
69+ errors = []
70+ if not isinstance (value , dict ) or len (value ) != 1 :
71+ errors .append ("Container must be a dict with exactly one key." )
72+ if len (value ) == 1 : # Only check the key if there is exactly one
73+ container_type = list (value .keys ())[0 ]
74+ if container_type not in ("section" , "subsection" , "unit" ):
75+ errors .append (f"Invalid container value: { container_type } " )
76+ if errors :
77+ raise serializers .ValidationError (errors )
78+ return value
79+
80+ def validate (self , attrs ):
81+ """
82+ Custom validation logic:
83+ parse the container dict to extract the container type.
84+ """
85+ container = attrs ["container" ]
86+ container_type = list (container .keys ())[0 ] # It is safe to do this after validate_container
87+ attrs ["container_type" ] = container_type
88+ attrs .pop ("container" ) # Remove the container field after processing
89+ return attrs
90+
91+
92+ class ContainerVersionSerializer (EntityVersionSerializer ): # pylint: disable=abstract-method
93+ """
94+ Serializer for container versions.
95+ """
96+ container = serializers .DictField (required = True )
97+
98+ def validate_container (self , value ):
99+ """
100+ Custom validation logic for the container field.
101+ Ensures that the container dict has exactly one key "children" which is a list of strings.
102+ """
103+ errors = []
104+ if not isinstance (value , dict ) or len (value ) != 1 :
105+ errors .append ("Container must be a dict with exactly one key." )
106+ if "children" not in value :
107+ errors .append ("Container must have a 'children' key." )
108+ if "children" in value and not isinstance (value ["children" ], list ):
109+ errors .append ("'children' must be a list." )
110+ if errors :
111+ raise serializers .ValidationError (errors )
112+ return value
113+
114+ def validate (self , attrs ):
115+ """
116+ Custom validation logic:
117+ parse the container dict to extract the children list.
118+ """
119+ children = attrs ["container" ]["children" ] # It is safe to do this after validate_container
120+ attrs ["children" ] = children
121+ attrs .pop ("container" ) # Remove the container field after processing
122+ return attrs
0 commit comments