Skip to content

Commit 6d5537c

Browse files
committed
INTPYTHON-769 Made embedded fields respect Field.db_column
1 parent 2d30fdb commit 6d5537c

File tree

9 files changed

+57
-19
lines changed

9 files changed

+57
-19
lines changed

django_mongodb_backend/fields/embedded_model.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,9 @@ def to_python(self, value):
9393
return value
9494
instance = self.embedded_model(
9595
**{
96-
field.attname: field.to_python(value[field.attname])
96+
field.attname: field.to_python(value[field.column])
9797
for field in self.embedded_model._meta.fields
98-
if field.attname in value
98+
if field.column in value
9999
}
100100
)
101101
instance._state.adding = False
@@ -122,7 +122,7 @@ def get_db_prep_save(self, embedded_instance, connection):
122122
# Exclude unset primary keys (e.g. {'id': None}).
123123
if field.primary_key and value is None:
124124
continue
125-
field_values[field.attname] = value
125+
field_values[field.column] = value
126126
# This instance will exist in the database soon.
127127
embedded_instance._state.adding = False
128128
return field_values
@@ -186,17 +186,17 @@ def get_transform(self, name):
186186

187187
def as_mql(self, compiler, connection, as_path=False):
188188
previous = self
189-
key_transforms = []
189+
columns = []
190190
while isinstance(previous, KeyTransform):
191-
key_transforms.insert(0, previous.key_name)
191+
columns.insert(0, previous.ref_field.column)
192192
previous = previous.lhs
193193
if as_path:
194194
mql = previous.as_mql(compiler, connection, as_path=True)
195-
mql_path = ".".join(key_transforms)
195+
mql_path = ".".join(columns)
196196
return f"{mql}.{mql_path}"
197197
mql = previous.as_mql(compiler, connection)
198-
for key in key_transforms:
199-
mql = {"$getField": {"input": mql, "field": key}}
198+
for column in columns:
199+
mql = {"$getField": {"input": mql, "field": column}}
200200
return mql
201201

202202
@property

django_mongodb_backend/fields/polymorphic_embedded_model.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,9 @@ def to_python(self, value):
121121
model_class = self._get_model_from_label(value.pop("_label"))
122122
instance = model_class(
123123
**{
124-
field.attname: field.to_python(value[field.attname])
124+
field.attname: field.to_python(value[field.column])
125125
for field in model_class._meta.fields
126-
if field.attname in value
126+
if field.column in value
127127
}
128128
)
129129
instance._state.adding = False
@@ -150,7 +150,7 @@ def get_db_prep_save(self, embedded_instance, connection):
150150
# Exclude unset primary keys (e.g. {'id': None}).
151151
if field.primary_key and value is None:
152152
continue
153-
field_values[field.attname] = value
153+
field_values[field.column] = value
154154
# Store the model's label to know the class to use for initializing
155155
# upon retrieval.
156156
field_values["_label"] = embedded_instance._meta.label

django_mongodb_backend/operations.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -184,14 +184,14 @@ def convert_embeddedmodelfield_value(self, value, expression, connection):
184184
if value is not None:
185185
# Apply database converters to each field of the embedded model.
186186
for field in expression.output_field.embedded_model._meta.fields:
187-
if field.attname not in value:
187+
if field.column not in value:
188188
continue
189189
field_expr = Expression(output_field=field)
190190
converters = connection.ops.get_db_converters(
191191
field_expr
192192
) + field_expr.get_db_converters(connection)
193193
for converter in converters:
194-
value[field.attname] = converter(value[field.attname], field_expr, connection)
194+
value[field.column] = converter(value[field.column], field_expr, connection)
195195
return value
196196

197197
def convert_jsonfield_value(self, value, expression, connection):
@@ -206,14 +206,14 @@ def convert_polymorphicembeddedmodelfield_value(self, value, expression, connect
206206
model_class = expression.output_field._get_model_from_label(value["_label"])
207207
# Apply database converters to each field of the embedded model.
208208
for field in model_class._meta.fields:
209-
if field.attname not in value:
209+
if field.column not in value:
210210
continue
211211
field_expr = Expression(output_field=field)
212212
converters = connection.ops.get_db_converters(
213213
field_expr
214214
) + field_expr.get_db_converters(connection)
215215
for converter in converters:
216-
value[field.attname] = converter(value[field.attname], field_expr, connection)
216+
value[field.column] = converter(value[field.column], field_expr, connection)
217217
return value
218218

219219
def convert_timefield_value(self, value, expression, connection):

docs/releases/5.2.x.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ Bug fixes
1919
that use a database converter, if the field isn't present in the data (e.g.
2020
data not written by Django, or after a field was added to an existing
2121
``EmbeddedModel``).
22+
- Made ``EmbeddedModel`` fields respect
23+
:attr:`~django.db.models.Field.db_column`.
2224

2325
Deprecated features
2426
-------------------

tests/model_fields_/models.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ class Holder(models.Model):
125125

126126

127127
class Data(EmbeddedModel):
128-
integer = models.IntegerField(db_column="custom_column")
128+
integer = models.IntegerField(db_column="integer_")
129129
auto_now = models.DateTimeField(auto_now=True)
130130
auto_now_add = models.DateTimeField(auto_now_add=True)
131131
json_value = models.JSONField()
@@ -175,7 +175,7 @@ def __str__(self):
175175

176176

177177
class Review(EmbeddedModel):
178-
title = models.CharField(max_length=255)
178+
title = models.CharField(max_length=255, db_column="title_")
179179
rating = models.DecimalField(max_digits=6, decimal_places=1)
180180

181181
def __str__(self):
@@ -261,7 +261,7 @@ def __str__(self):
261261

262262

263263
class Cat(EmbeddedModel):
264-
name = models.CharField(max_length=100)
264+
name = models.CharField(max_length=100, db_column="name_")
265265
purs = models.BooleanField(default=True)
266266
weight = models.DecimalField(max_digits=4, decimal_places=2, blank=True, null=True)
267267
favorite_toy = PolymorphicEmbeddedModelField(["Mouse"], blank=True, null=True)

tests/model_fields_/test_embedded_model.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,18 @@ def test_missing_field_in_data(self):
114114
this case, integer is an IntegerField) doesn't crash.
115115
"""
116116
Holder.objects.create(data=Data(integer=5))
117-
connection.database.model_fields__holder.update_many({}, {"$unset": {"data.integer": ""}})
117+
connection.database.model_fields__holder.update_many({}, {"$unset": {"data.integer_": ""}})
118118
self.assertIsNone(Holder.objects.first().data.integer)
119119

120+
def test_embedded_model_field_respects_db_column(self):
121+
"""
122+
EmbeddedModel data respects Field.db_column. In this case, Data.integer
123+
has db_column="integer_".
124+
"""
125+
obj = Holder.objects.create(data=Data(integer=5))
126+
query = connection.database.model_fields__holder.find({"_id": obj.pk})
127+
self.assertEqual(query[0]["data"]["integer_"], 5)
128+
120129

121130
class QueryingTests(TestCase):
122131
@classmethod

tests/model_fields_/test_embedded_model_array.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,15 @@ def test_missing_field_in_data(self):
7474
)
7575
self.assertIsNone(Movie.objects.first().reviews[0].rating)
7676

77+
def test_embedded_model_field_respects_db_column(self):
78+
"""
79+
EmbeddedModel data respects Field.db_column. In this case,
80+
Review.title has db_column="title_".
81+
"""
82+
obj = Movie.objects.create(title="Lion King", reviews=[Review(title="Awesome", rating=10)])
83+
query = connection.database.model_fields__movie.find({"_id": obj.pk})
84+
self.assertEqual(query[0]["reviews"][0]["title_"], "Awesome")
85+
7786

7887
class QueryingTests(TestCase):
7988
@classmethod

tests/model_fields_/test_polymorphic_embedded_model.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,15 @@ def test_missing_field_in_data(self):
101101
connection.database.model_fields__person.update_many({}, {"$unset": {"pet.weight": ""}})
102102
self.assertIsNone(Person.objects.first().pet.weight)
103103

104+
def test_embedded_model_field_respects_db_column(self):
105+
"""
106+
EmbeddedModel data respects Field.db_column. In this case, Cat.name
107+
has db_column="name_".
108+
"""
109+
obj = Person.objects.create(pet=Cat(name="Phoebe"))
110+
query = connection.database.model_fields__person.find({"_id": obj.pk})
111+
self.assertEqual(query[0]["pet"]["name_"], "Phoebe")
112+
104113

105114
class QueryingTests(TestCase):
106115
@classmethod

tests/model_fields_/test_polymorphic_embedded_model_array.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,15 @@ def test_missing_field_in_data(self):
7272
connection.database.model_fields__owner.update_many({}, {"$unset": {"pets.$[].weight": ""}})
7373
self.assertIsNone(Owner.objects.first().pets[0].weight)
7474

75+
def test_embedded_model_field_respects_db_column(self):
76+
"""
77+
EmbeddedModel data respects Field.db_column. In this case, Cat.name
78+
has db_column="name_".
79+
"""
80+
obj = Owner.objects.create(name="Bob", pets=[Cat(name="Phoebe", weight="3.5")])
81+
query = connection.database.model_fields__owner.find({"_id": obj.pk})
82+
self.assertEqual(query[0]["pets"][0]["name_"], "Phoebe")
83+
7584

7685
class QueryingTests(TestCase):
7786
@classmethod

0 commit comments

Comments
 (0)