Skip to content

Commit 43bf45c

Browse files
committed
INTPYTHON-769 Made embedded fields respect Field.db_column
1 parent baf66e3 commit 43bf45c

File tree

9 files changed

+58
-20
lines changed

9 files changed

+58
-20
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
``PolymorphicEmbeddedModelField`` if an embedded model field that uses a
2020
converter isn't present in the data (e.g. data not written by Django, or
2121
after a field was added to an existing embedded model).
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
@@ -107,6 +107,15 @@ def test_pre_save(self):
107107
self.assertEqual(obj.data.auto_now_add, auto_now_add)
108108
self.assertGreater(obj.data.auto_now, auto_now_two)
109109

110+
def test_embedded_model_respects_db_column(self):
111+
"""
112+
EmbeddedModel data respects Field.db_column. In this case, Data.integer
113+
has db_column="integer_".
114+
"""
115+
obj = Holder.objects.create(data=Data(integer=5))
116+
query = connection.database.model_fields__holder.find({"_id": obj.pk})
117+
self.assertEqual(query[0]["data"]["integer_"], 5)
118+
110119

111120
class QueryingTests(TestCase):
112121
@classmethod
@@ -231,7 +240,7 @@ def test_missing_field_in_data(self):
231240
(e.g. data not written by Django) that uses a database converter (in
232241
this case, integer is an IntegerField) doesn't crash.
233242
"""
234-
connection.database.model_fields__holder.update_many({}, {"$unset": {"data.integer": ""}})
243+
connection.database.model_fields__holder.update_many({}, {"$unset": {"data.integer_": ""}})
235244
self.assertIsNone(Holder.objects.first().data.integer)
236245

237246

tests/model_fields_/test_embedded_model_array.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,15 @@ def test_save_load_null(self):
6262
movie = Movie.objects.get(title="Lion King")
6363
self.assertIsNone(movie.reviews)
6464

65+
def test_embedded_model_respects_db_column(self):
66+
"""
67+
EmbeddedModel data respects Field.db_column. In this case,
68+
Review.title has db_column="title_".
69+
"""
70+
obj = Movie.objects.create(title="Lion King", reviews=[Review(title="Awesome", rating=10)])
71+
query = connection.database.model_fields__movie.find({"_id": obj.pk})
72+
self.assertEqual(query[0]["reviews"][0]["title_"], "Awesome")
73+
6574

6675
class QueryingTests(TestCase):
6776
@classmethod

tests/model_fields_/test_polymorphic_embedded_model.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,15 @@ def test_pre_save(self):
9191
# simultaneously.
9292
self.assertAlmostEqual(updated_at, created_at, delta=timedelta(microseconds=1000))
9393

94+
def test_embedded_model_respects_db_column(self):
95+
"""
96+
EmbeddedModel data respects Field.db_column. In this case, Cat.name
97+
has db_column="name_".
98+
"""
99+
obj = Person.objects.create(pet=Cat(name="Phoebe"))
100+
query = connection.database.model_fields__person.find({"_id": obj.pk})
101+
self.assertEqual(query[0]["pet"]["name_"], "Phoebe")
102+
94103

95104
class QueryingTests(TestCase):
96105
@classmethod

tests/model_fields_/test_polymorphic_embedded_model_array.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from decimal import Decimal
22

33
from django.core.exceptions import FieldDoesNotExist
4-
from django.db import models
4+
from django.db import connection, models
55
from django.test import SimpleTestCase, TestCase
66
from django.test.utils import isolate_apps
77

@@ -62,6 +62,15 @@ def test_save_load_null(self):
6262
owner = Owner.objects.get(name="Bob")
6363
self.assertIsNone(owner.pets)
6464

65+
def test_embedded_model_respects_db_column(self):
66+
"""
67+
EmbeddedModel data respects Field.db_column. In this case, Cat.name
68+
has db_column="name_".
69+
"""
70+
obj = Owner.objects.create(name="Bob", pets=[Cat(name="Phoebe", weight="3.5")])
71+
query = connection.database.model_fields__owner.find({"_id": obj.pk})
72+
self.assertEqual(query[0]["pets"][0]["name_"], "Phoebe")
73+
6574

6675
class QueryingTests(TestCase):
6776
@classmethod

0 commit comments

Comments
 (0)