Skip to content

Commit 12d61ad

Browse files
committed
remove generic support
1 parent 3597355 commit 12d61ad

File tree

3 files changed

+51
-126
lines changed

3 files changed

+51
-126
lines changed

django_mongodb_backend/fields/embedded_model.py

Lines changed: 18 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
from importlib import import_module
2-
3-
from django.db import IntegrityError, models
1+
from django.db import models
42
from django.db.models.fields.related import lazy_related_operation
53

64

75
class EmbeddedModelField(models.Field):
86
"""Field that stores a model instance."""
97

10-
def __init__(self, embedded_model=None, *args, **kwargs):
8+
def __init__(self, embedded_model, *args, **kwargs):
119
"""
1210
`embedded_model` is the model class of the instance that will be
1311
stored. Like other relational fields, it may also be passed as a
@@ -22,8 +20,7 @@ def deconstruct(self):
2220
path = path.replace(
2321
"django_mongodb_backend.fields.embedded_model", "django_mongodb.fields"
2422
)
25-
if self.embedded_model:
26-
kwargs["embedded_model"] = self.embedded_model
23+
kwargs["embedded_model"] = self.embedded_model
2724
return name, path, args, kwargs
2825

2926
def get_internal_type(self):
@@ -52,60 +49,26 @@ def _resolve_lookup(_, resolved_model):
5249

5350
model = property(lambda self: self._model, _set_model)
5451

55-
def stored_model(self, column_values):
56-
"""
57-
Return the fixed embedded_model this field was initialized
58-
with (typed embedding) or tries to determine the model from
59-
_module / _model keys stored together with column_values
60-
(untyped embedding).
61-
62-
Give precedence to the field's definition model, as silently using a
63-
differing serialized one could hide some data integrity problems.
64-
65-
Note that a single untyped EmbeddedModelField may process
66-
instances of different models (especially when used as a type
67-
of a collection field).
68-
"""
69-
module = column_values.pop("_module", None)
70-
model = column_values.pop("_model", None)
71-
if self.embedded_model is not None:
72-
return self.embedded_model
73-
if module is not None:
74-
return getattr(import_module(module), model)
75-
raise IntegrityError(
76-
"Untyped EmbeddedModelField trying to load data without serialized model class info."
77-
)
78-
7952
def from_db_value(self, value, expression, connection):
8053
return self.to_python(value)
8154

8255
def to_python(self, value):
8356
"""
8457
Passes embedded model fields' values through embedded fields
85-
to_python methods and reinstiatates the embedded instance.
86-
87-
We expect to receive a field.attname => value dict together
88-
with a model class from back-end database deconversion (which
89-
needs to know fields of the model beforehand).
58+
to_python() and reinstiatates the embedded instance.
9059
"""
91-
# Either the model class has already been determined during
92-
# deconverting values from the database or we've got a dict
93-
# from a deserializer that may contain model class info.
94-
if isinstance(value, tuple):
95-
embedded_model, attribute_values = value
96-
elif isinstance(value, dict):
97-
embedded_model = self.stored_model(value)
98-
attribute_values = value
99-
else:
60+
if value is None:
61+
return None
62+
if not isinstance(value, dict):
10063
return value
10164
# Create the model instance.
102-
instance = embedded_model(
65+
instance = self.embedded_model(
10366
**{
10467
# Pass values through respective fields' to_python(), leaving
10568
# fields for which no value is specified uninitialized.
106-
field.attname: field.to_python(attribute_values[field.attname])
107-
for field in embedded_model._meta.fields
108-
if field.attname in attribute_values
69+
field.attname: field.to_python(value[field.attname])
70+
for field in self.embedded_model._meta.fields
71+
if field.attname in value
10972
}
11073
)
11174
instance._state.adding = False
@@ -117,21 +80,17 @@ def get_db_prep_save(self, embedded_instance, connection):
11780
fields and passes a field => value mapping down to database
11881
type conversions.
11982
120-
The embedded instance will be saved as a column => value dict
121-
in the end (possibly augmented with info about instance's model
122-
for untyped embedding), but because we need to apply database
123-
type conversions on embedded instance fields' values and for
124-
these we need to know fields those values come from, we need to
125-
entrust the database layer with creating the dict.
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.
12687
"""
12788
if embedded_instance is None:
12889
return None
129-
# The field's value should be an instance of the model given in
130-
# its declaration or at least of some model.
131-
embedded_model = self.embedded_model or models.Model
132-
if not isinstance(embedded_instance, embedded_model):
90+
if not isinstance(embedded_instance, self.embedded_model):
13391
raise TypeError(
134-
f"Expected instance of type {embedded_model!r}, not {type(embedded_instance)!r}."
92+
f"Expected instance of type {self.embedded_model!r}, not "
93+
f"{type(embedded_instance)!r}."
13594
)
13695
# Apply pre_save() and get_db_prep_save() of embedded instance
13796
# fields, create the field => value mapping to be passed to
@@ -146,14 +105,6 @@ def get_db_prep_save(self, embedded_instance, connection):
146105
if field.primary_key and value is None:
147106
continue
148107
field_values[field.attname] = value
149-
if self.embedded_model is None:
150-
# Untyped fields must store model info alongside values.
151-
field_values.update(
152-
(
153-
("_module", embedded_instance.__class__.__module__),
154-
("_model", embedded_instance.__class__.__name__),
155-
)
156-
)
157108
# This instance will exist in the database soon.
158109
# TODO.XXX: Ensure that this doesn't cause race conditions.
159110
embedded_instance._state.adding = False

tests/model_fields_/models.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ class DecimalParent(models.Model):
112112

113113
class EmbeddedModelFieldModel(models.Model):
114114
simple = EmbeddedModelField("EmbeddedModel", null=True, blank=True)
115-
untyped = EmbeddedModelField(null=True, blank=True)
116115
decimal_parent = EmbeddedModelField(DecimalParent, null=True, blank=True)
117116

118117

tests/model_fields_/test_embedded_model.py

Lines changed: 33 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
from decimal import Decimal
33

44
from django.core.exceptions import ValidationError
5-
from django.db import models
65
from django.test import SimpleTestCase, TestCase
76

87
from django_mongodb_backend.fields import EmbeddedModelField
@@ -18,98 +17,74 @@
1817

1918
class MethodTests(SimpleTestCase):
2019
def test_deconstruct(self):
21-
field = EmbeddedModelField()
22-
name, path, args, kwargs = field.deconstruct()
23-
self.assertEqual(path, "django_mongodb.fields.EmbeddedModelField")
24-
self.assertEqual(args, [])
25-
self.assertEqual(kwargs, {})
26-
27-
def test_deconstruct_with_model(self):
2820
field = EmbeddedModelField("EmbeddedModel", null=True)
2921
name, path, args, kwargs = field.deconstruct()
3022
self.assertEqual(path, "django_mongodb.fields.EmbeddedModelField")
3123
self.assertEqual(args, [])
3224
self.assertEqual(kwargs, {"embedded_model": "EmbeddedModel", "null": True})
3325

3426
def test_validate(self):
35-
instance = EmbeddedModelFieldModel(simple=EmbeddedModel(someint=None))
27+
obj = EmbeddedModelFieldModel(simple=EmbeddedModel(someint=None))
3628
# This isn't quite right because "someint" is the field that's non-null.
3729
msg = "{'simple': ['This field cannot be null.']}"
3830
with self.assertRaisesMessage(ValidationError, msg):
39-
instance.full_clean()
31+
obj.full_clean()
4032

4133

4234
class QueryingTests(TestCase):
4335
def assertEqualDatetime(self, d1, d2):
44-
"""Compares d1 and d2, ignoring microseconds."""
36+
"""Compare d1 and d2, ignoring microseconds."""
4537
self.assertEqual(d1.replace(microsecond=0), d2.replace(microsecond=0))
4638

4739
def assertNotEqualDatetime(self, d1, d2):
4840
self.assertNotEqual(d1.replace(microsecond=0), d2.replace(microsecond=0))
4941

5042
def test_save_load(self):
5143
EmbeddedModelFieldModel.objects.create(simple=EmbeddedModel(someint="5"))
52-
instance = EmbeddedModelFieldModel.objects.get()
53-
self.assertIsInstance(instance.simple, EmbeddedModel)
44+
obj = EmbeddedModelFieldModel.objects.get()
45+
self.assertIsInstance(obj.simple, EmbeddedModel)
5446
# Make sure get_prep_value is called.
55-
self.assertEqual(instance.simple.someint, 5)
47+
self.assertEqual(obj.simple.someint, 5)
5648
# Primary keys should not be populated...
57-
self.assertEqual(instance.simple.id, None)
49+
self.assertEqual(obj.simple.id, None)
5850
# ... unless set explicitly.
59-
instance.simple.id = instance.id
60-
instance.save()
61-
instance = EmbeddedModelFieldModel.objects.get()
62-
self.assertEqual(instance.simple.id, instance.id)
51+
obj.simple.id = obj.id
52+
obj.save()
53+
obj = EmbeddedModelFieldModel.objects.get()
54+
self.assertEqual(obj.simple.id, obj.id)
6355

64-
def test_save_load_untyped(self):
65-
EmbeddedModelFieldModel.objects.create(simple=EmbeddedModel(someint="5"))
66-
instance = EmbeddedModelFieldModel.objects.get()
67-
self.assertIsInstance(instance.simple, EmbeddedModel)
68-
# Make sure get_prep_value is called.
69-
self.assertEqual(instance.simple.someint, 5)
70-
# Primary keys should not be populated...
71-
self.assertEqual(instance.simple.id, None)
72-
# ... unless set explicitly.
73-
instance.simple.id = instance.id
74-
instance.save()
75-
instance = EmbeddedModelFieldModel.objects.get()
76-
self.assertEqual(instance.simple.id, instance.id)
56+
def test_save_load_null(self):
57+
EmbeddedModelFieldModel.objects.create(simple=None)
58+
obj = EmbeddedModelFieldModel.objects.get()
59+
self.assertIsNone(obj.simple)
7760

78-
def _test_pre_save(self, instance, get_field):
79-
# Field.pre_save() is called on embedded model fields.
61+
def test_pre_save(self):
62+
"""Field.pre_save() is called on embedded model fields."""
63+
obj = EmbeddedModelFieldModel(simple=EmbeddedModel())
8064

81-
instance.save()
82-
auto_now = get_field(instance).auto_now
83-
auto_now_add = get_field(instance).auto_now_add
65+
obj.save()
66+
auto_now = obj.simple.auto_now
67+
auto_now_add = obj.simple.auto_now_add
8468
self.assertNotEqual(auto_now, None)
8569
self.assertNotEqual(auto_now_add, None)
8670

8771
time.sleep(1) # FIXME
88-
instance.save()
89-
self.assertNotEqualDatetime(get_field(instance).auto_now, get_field(instance).auto_now_add)
72+
obj.save()
73+
self.assertNotEqualDatetime(obj.simple.auto_now, obj.simple.auto_now_add)
9074

91-
instance = EmbeddedModelFieldModel.objects.get()
92-
instance.save()
75+
obj = EmbeddedModelFieldModel.objects.get()
76+
obj.save()
9377
# auto_now_add shouldn't have changed now, but auto_now should.
94-
self.assertEqualDatetime(get_field(instance).auto_now_add, auto_now_add)
95-
self.assertGreater(get_field(instance).auto_now, auto_now)
96-
97-
def test_pre_save(self):
98-
obj = EmbeddedModelFieldModel(simple=EmbeddedModel())
99-
self._test_pre_save(obj, lambda instance: instance.simple)
100-
101-
def test_pre_save_untyped(self):
102-
obj = EmbeddedModelFieldModel(untyped=EmbeddedModel())
103-
self._test_pre_save(obj, lambda instance: instance.untyped)
78+
self.assertEqualDatetime(obj.simple.auto_now_add, auto_now_add)
79+
self.assertGreater(obj.simple.auto_now, auto_now)
10480

10581
def test_error_messages(self):
106-
for model_kwargs, expected in (
107-
({"simple": 42}, EmbeddedModel),
108-
({"untyped": 42}, models.Model),
109-
):
110-
msg = "Expected instance of type %r" % expected
111-
with self.assertRaisesMessage(TypeError, msg):
112-
EmbeddedModelFieldModel(**model_kwargs).save()
82+
msg = (
83+
"Expected instance of type <class 'model_fields_.models.EmbeddedModel'>, "
84+
"not <class 'int'>."
85+
)
86+
with self.assertRaisesMessage(TypeError, msg):
87+
EmbeddedModelFieldModel(simple=42).save()
11388

11489
def test_foreign_key_in_embedded_object(self):
11590
simple = EmbeddedModel(some_relation=Target.objects.create(index=1))

0 commit comments

Comments
 (0)