1
1
from typing import Any , Optional
2
2
3
+ from django .apps import apps
3
4
from django .core .exceptions import FieldDoesNotExist
4
5
from django .db .models import Field , Model
6
+ from django .http import HttpRequest
5
7
from django .urls import NoReverseMatch , reverse
6
8
7
9
from cms .models import CMSPlugin
8
10
from cms .plugin_pool import plugin_pool
9
11
12
+ from djangocms_rest .utils import get_absolute_frontend_url
10
13
from rest_framework import serializers
11
14
12
15
13
16
def serialize_fk (
14
- related_model : type [CMSPlugin ], pk : Any , obj : Optional [Model ] = None
17
+ request : HttpRequest ,
18
+ related_model : type [CMSPlugin ],
19
+ pk : Any ,
20
+ obj : Optional [Model ] = None ,
15
21
) -> dict [str , Any ]:
16
22
"""
17
23
Serializes a foreign key reference to a related model as a URL or identifier.
@@ -33,12 +39,14 @@ def serialize_fk(
33
39
if hasattr (related_model , "get_api_endpoint" ):
34
40
if obj is None :
35
41
obj = related_model .objects .filter (pk = pk ).first ()
36
- return obj .get_api_endpoint ()
42
+ return get_absolute_frontend_url ( request , obj .get_api_endpoint () )
37
43
38
44
# Second choice: Use DRF naming conventions to build the default API URL for the related model
39
45
model_name = related_model ._meta .model_name
40
46
try :
41
- return reverse (f"{ model_name } _details" , args = (pk ,))
47
+ return get_absolute_frontend_url (
48
+ request , reverse (f"{ model_name } _details" , args = (pk ,))
49
+ )
42
50
except NoReverseMatch :
43
51
pass
44
52
@@ -47,6 +55,46 @@ def serialize_fk(
47
55
return f"{ app_name } .{ model_name } :{ pk } "
48
56
49
57
58
+ def serialize_soft_refs (request : HttpRequest , data : Any ) -> Any :
59
+ """
60
+ Serialize soft references in a dictionary or list.
61
+
62
+ This function recursively traverses the input data structure and serializes
63
+ any soft references (dictionaries with 'model' and 'pk' keys) into a more
64
+ usable format.
65
+
66
+ Args:
67
+ data (Any): The input data structure, which can be a dict, list, or other types.
68
+
69
+ Returns:
70
+ Any: The serialized data structure with soft references replaced.
71
+ """
72
+ if isinstance (data , list ):
73
+ return [serialize_soft_refs (request , item ) for item in data ]
74
+ for key , value in data .items ():
75
+ if isinstance (value , dict ) and set (value .keys ()) == {"model" , "pk" }:
76
+ app_name , model_name = value ["model" ].split ("." , 1 )
77
+ model_class = apps .get_model (app_name , model_name )
78
+ pk = value ["pk" ]
79
+ data [key ] = serialize_fk (request , model_class , pk )
80
+ elif key == "attrs" and isinstance (value , dict ) and value .get ("data-cms-href" ):
81
+ model , pk = value ["data-cms-href" ].split (":" , 1 )
82
+ app_name , model_name = model .split ("." , 1 )
83
+ model_class = apps .get_model (app_name , model_name )
84
+ value ["data-cms-href" ] = serialize_fk (request , model_class , pk )
85
+ elif isinstance (value , dict ) and "internal_link" in value :
86
+ model , pk = value ["internal_link" ].split (":" , 1 )
87
+ app_name , model_name = model .split ("." , 1 )
88
+ model_class = apps .get_model (app_name , model_name )
89
+ data [key ] = serialize_fk (request , model_class , pk )
90
+ elif isinstance (value , dict ) and "file_link" in value :
91
+ model_class = apps .get_model ("filer" , "file" )
92
+ data [key ] = serialize_fk (request , model_class , value ["file_link" ])
93
+ elif isinstance (value , (dict , list )):
94
+ data [key ] = serialize_soft_refs (request , value )
95
+ return data
96
+
97
+
50
98
base_exclude = {
51
99
"id" ,
52
100
"placeholder" ,
@@ -58,20 +106,36 @@ def serialize_fk(
58
106
}
59
107
#: Excluded fields for plugin serialization
60
108
109
+ JSON_FIELDS = tuple (
110
+ field
111
+ for field , value in serializers .ModelSerializer .serializer_field_mapping .items ()
112
+ if value is serializers .JSONField
113
+ )
114
+
61
115
62
116
class GenericPluginSerializer (serializers .ModelSerializer ):
117
+ def __init__ (self , * args , ** kwargs ):
118
+ super ().__init__ (* args , ** kwargs )
119
+ self .request = self .context .get ("request" , None )
120
+
63
121
def to_representation (self , instance : CMSPlugin ):
122
+ request = getattr (self , "request" , None )
123
+
64
124
ret = super ().to_representation (instance )
65
125
for field in self .Meta .model ._meta .get_fields ():
66
126
if field .is_relation and not field .many_to_many and not field .one_to_many :
67
127
if field .name in ret and getattr (instance , field .name , None ):
68
128
ret [field .name ] = serialize_fk (
129
+ request ,
69
130
field .related_model ,
70
131
getattr (instance , field .name + "_id" ),
71
132
obj = getattr (instance , field .name )
72
133
if field .is_cached (instance )
73
134
else None ,
74
135
)
136
+ elif isinstance (field , JSON_FIELDS ):
137
+ # If the field is a subclass of JSONField, serialize its value directly
138
+ ret [field .name ] = serialize_soft_refs (request , ret [field .name ])
75
139
return ret
76
140
77
141
0 commit comments