1
1
from django .db import models
2
2
from django .db .models .fields .related import lazy_related_operation
3
+ from django .db .models .lookups import Transform
3
4
4
5
5
6
class EmbeddedModelField (models .Field ):
@@ -31,13 +32,8 @@ def _set_model(self, model):
31
32
Resolve embedded model class once the field knows the model it belongs
32
33
to.
33
34
34
- If the model argument passed to __init__() was a string, resolve that
35
- string to the corresponding model class, similar to relation fields.
36
- However, we need to know our own model to generate a valid key
37
- for the embedded model class lookup and EmbeddedModelFields are
38
- not contributed_to_class if used in iterable fields. Thus the
39
- collection field sets this field's "model" attribute in its
40
- contribute_to_class().
35
+ If __init__()'s embedded_model argument is a string, resolve it to the
36
+ corresponding model class, similar to relation fields.
41
37
"""
42
38
self ._model = model
43
39
if model is not None and isinstance (self .embedded_model , str ):
@@ -54,8 +50,8 @@ def from_db_value(self, value, expression, connection):
54
50
55
51
def to_python (self , value ):
56
52
"""
57
- Passes embedded model fields' values through embedded fields
58
- to_python() and reinstiatates the embedded instance.
53
+ Pass embedded model fields' values through each field's to_python() and
54
+ reinstiatate the embedded instance.
59
55
"""
60
56
if value is None :
61
57
return None
@@ -76,14 +72,8 @@ def to_python(self, value):
76
72
77
73
def get_db_prep_save (self , embedded_instance , connection ):
78
74
"""
79
- Apply pre_save() and get_db_prep_save() of embedded instance
80
- fields and passes a field => value mapping down to database
81
- type conversions.
82
-
83
- The embedded instance will be saved as a column => value dict, but
84
- because we need to apply database type conversions on embedded instance
85
- fields' values and for these we need to know fields those values come
86
- from, we need to entrust the database layer with creating the dict.
75
+ Apply pre_save() and get_db_prep_save() of embedded instance fields and
76
+ create the {field: value} dict to be saved.
87
77
"""
88
78
if embedded_instance is None :
89
79
return None
@@ -106,14 +96,48 @@ def get_db_prep_save(self, embedded_instance, connection):
106
96
continue
107
97
field_values [field .attname ] = value
108
98
# This instance will exist in the database soon.
109
- # TODO.XXX : Ensure that this doesn't cause race conditions.
99
+ # TODO: Ensure that this doesn't cause race conditions.
110
100
embedded_instance ._state .adding = False
111
101
return field_values
112
102
103
+ def get_transform (self , name ):
104
+ transform = super ().get_transform (name )
105
+ if transform :
106
+ return transform
107
+ return KeyTransformFactory (name )
108
+
113
109
def validate (self , value , model_instance ):
114
110
super ().validate (value , model_instance )
115
111
if self .embedded_model is None :
116
112
return
117
113
for field in self .embedded_model ._meta .fields :
118
114
attname = field .attname
119
115
field .validate (getattr (value , attname ), model_instance )
116
+
117
+
118
+ class KeyTransform (Transform ):
119
+ def __init__ (self , key_name , * args , ** kwargs ):
120
+ super ().__init__ (* args , ** kwargs )
121
+ self .key_name = str (key_name )
122
+
123
+ def preprocess_lhs (self , compiler , connection ):
124
+ key_transforms = [self .key_name ]
125
+ previous = self .lhs
126
+ while isinstance (previous , KeyTransform ):
127
+ key_transforms .insert (0 , previous .key_name )
128
+ previous = previous .lhs
129
+ mql = previous .as_mql (compiler , connection )
130
+ return mql , key_transforms
131
+
132
+ def as_mql (self , compiler , connection ):
133
+ mql , key_transforms = self .preprocess_lhs (compiler , connection )
134
+ transforms = "." .join (key_transforms )
135
+ return f"{ mql } .{ transforms } "
136
+
137
+
138
+ class KeyTransformFactory :
139
+ def __init__ (self , key_name ):
140
+ self .key_name = key_name
141
+
142
+ def __call__ (self , * args , ** kwargs ):
143
+ return KeyTransform (self .key_name , * args , ** kwargs )
0 commit comments