Skip to content

Commit 07d274c

Browse files
committed
wip schema changes
1 parent a7451fa commit 07d274c

File tree

5 files changed

+331
-20
lines changed

5 files changed

+331
-20
lines changed

django_mongodb/schema.py

Lines changed: 60 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from pymongo import ASCENDING, DESCENDING
66
from pymongo.operations import IndexModel
77

8+
from .fields import EmbeddedModelField
89
from .query import wrap_database_errors
910
from .utils import OperationCollector
1011

@@ -29,25 +30,40 @@ def create_model(self, model):
2930
if field.remote_field.through._meta.auto_created:
3031
self.create_model(field.remote_field.through)
3132

32-
def _create_model_indexes(self, model):
33+
def _create_model_indexes(self, model, column_prefix="", parent_model=None):
3334
"""
3435
Create all indexes (field indexes & uniques, Meta.index_together,
3536
Meta.unique_together, Meta.constraints, Meta.indexes) for the model.
37+
38+
If this is a recursive call to due to an embedded model, `column_prefix`
39+
tracks the path that must be prepended to the index's column, and
40+
`parent_model` tracks the collection to add the index/constraint to.
3641
"""
3742
if not model._meta.managed or model._meta.proxy or model._meta.swapped:
3843
return
3944
# Field indexes and uniques
4045
for field in model._meta.local_fields:
46+
if isinstance(field, EmbeddedModelField):
47+
new_path = f"{column_prefix}{field.column}."
48+
self._create_model_indexes(
49+
field.embedded_model, parent_model=parent_model or model, column_prefix=new_path
50+
)
4151
if self._field_should_be_indexed(model, field):
42-
self._add_field_index(model, field)
52+
self._add_field_index(parent_model or model, field, column_prefix=column_prefix)
4353
elif self._field_should_have_unique(field):
44-
self._add_field_unique(model, field)
54+
self._add_field_unique(parent_model or model, field, column_prefix=column_prefix)
4555
# Meta.index_together (RemovedInDjango51Warning)
4656
for field_names in model._meta.index_together:
4757
self._add_composed_index(model, field_names)
4858
# Meta.unique_together
4959
if model._meta.unique_together:
50-
self.alter_unique_together(model, [], model._meta.unique_together)
60+
self.alter_unique_together(
61+
model,
62+
[],
63+
model._meta.unique_together,
64+
column_prefix=column_prefix,
65+
parent_model=parent_model,
66+
)
5167
# Meta.constraints
5268
for constraint in model._meta.constraints:
5369
self.add_constraint(model, constraint)
@@ -147,7 +163,9 @@ def alter_index_together(self, model, old_index_together, new_index_together):
147163
for field_names in news.difference(olds):
148164
self._add_composed_index(model, field_names)
149165

150-
def alter_unique_together(self, model, old_unique_together, new_unique_together):
166+
def alter_unique_together(
167+
self, model, old_unique_together, new_unique_together, column_prefix="", parent_model=None
168+
):
151169
olds = {tuple(fields) for fields in old_unique_together}
152170
news = {tuple(fields) for fields in new_unique_together}
153171
# Deleted uniques
@@ -160,11 +178,19 @@ def alter_unique_together(self, model, old_unique_together, new_unique_together)
160178
# Created uniques
161179
for field_names in news.difference(olds):
162180
columns = [model._meta.get_field(field).column for field in field_names]
163-
name = str(self._unique_constraint_name(model._meta.db_table, columns))
181+
name = str(
182+
self._unique_constraint_name(
183+
model._meta.db_table, [column_prefix + col for col in columns]
184+
)
185+
)
164186
constraint = UniqueConstraint(fields=field_names, name=name)
165-
self.add_constraint(model, constraint)
187+
self.add_constraint(
188+
model, constraint, parent_model=parent_model, column_prefix=column_prefix
189+
)
166190

167-
def add_index(self, model, index, field=None, unique=False):
191+
def add_index(
192+
self, model, index, *, field=None, unique=False, column_prefix="", parent_model=None
193+
):
168194
if index.contains_expressions:
169195
return
170196
kwargs = {}
@@ -176,7 +202,8 @@ def add_index(self, model, index, field=None, unique=False):
176202
# Indexing on $type matches the value of most SQL databases by
177203
# allowing multiple null values for the unique constraint.
178204
if field:
179-
filter_expression[field.column].update({"$type": field.db_type(self.connection)})
205+
column = column_prefix + field.column
206+
filter_expression[column].update({"$type": field.db_type(self.connection)})
180207
else:
181208
for field_name, _ in index.fields_orders:
182209
field_ = model._meta.get_field(field_name)
@@ -186,16 +213,20 @@ def add_index(self, model, index, field=None, unique=False):
186213
if filter_expression:
187214
kwargs["partialFilterExpression"] = filter_expression
188215
index_orders = (
189-
[(field.column, ASCENDING)]
216+
[(column_prefix + field.column, ASCENDING)]
190217
if field
191218
else [
192219
# order is "" if ASCENDING or "DESC" if DESCENDING (see
193220
# django.db.models.indexes.Index.fields_orders).
194-
(model._meta.get_field(field_name).column, ASCENDING if order == "" else DESCENDING)
221+
(
222+
column_prefix + model._meta.get_field(field_name).column,
223+
ASCENDING if order == "" else DESCENDING,
224+
)
195225
for field_name, order in index.fields_orders
196226
]
197227
)
198228
idx = IndexModel(index_orders, name=index.name, **kwargs)
229+
model = parent_model or model
199230
self.get_collection(model._meta.db_table).create_indexes([idx])
200231

201232
def _add_composed_index(self, model, field_names):
@@ -204,11 +235,11 @@ def _add_composed_index(self, model, field_names):
204235
idx.set_name_with_model(model)
205236
self.add_index(model, idx)
206237

207-
def _add_field_index(self, model, field):
238+
def _add_field_index(self, model, field, *, column_prefix=""):
208239
"""Add an index on a field with db_index=True."""
209-
index = Index(fields=[field.name])
210-
index.name = self._create_index_name(model._meta.db_table, [field.column])
211-
self.add_index(model, index, field=field)
240+
index = Index(fields=[column_prefix + field.name])
241+
index.name = self._create_index_name(model._meta.db_table, [column_prefix + field.column])
242+
self.add_index(model, index, field=field, column_prefix=column_prefix)
212243

213244
def remove_index(self, model, index):
214245
if index.contains_expressions:
@@ -260,7 +291,7 @@ def _remove_field_index(self, model, field):
260291
)
261292
collection.drop_index(index_names[0])
262293

263-
def add_constraint(self, model, constraint, field=None):
294+
def add_constraint(self, model, constraint, field=None, column_prefix="", parent_model=None):
264295
if isinstance(constraint, UniqueConstraint) and self._unique_supported(
265296
condition=constraint.condition,
266297
deferrable=constraint.deferrable,
@@ -273,12 +304,21 @@ def add_constraint(self, model, constraint, field=None):
273304
name=constraint.name,
274305
condition=constraint.condition,
275306
)
276-
self.add_index(model, idx, field=field, unique=True)
307+
self.add_index(
308+
model,
309+
idx,
310+
field=field,
311+
unique=True,
312+
column_prefix=column_prefix,
313+
parent_model=parent_model,
314+
)
277315

278-
def _add_field_unique(self, model, field):
279-
name = str(self._unique_constraint_name(model._meta.db_table, [field.column]))
316+
def _add_field_unique(self, model, field, column_prefix=""):
317+
name = str(
318+
self._unique_constraint_name(model._meta.db_table, [column_prefix + field.column])
319+
)
280320
constraint = UniqueConstraint(fields=[field.name], name=name)
281-
self.add_constraint(model, constraint, field=field)
321+
self.add_constraint(model, constraint, field=field, column_prefix=column_prefix)
282322

283323
def remove_constraint(self, model, constraint):
284324
if isinstance(constraint, UniqueConstraint) and self._unique_supported(

tests/model_fields_/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class EmbeddedModel(models.Model):
3434
class Address(models.Model):
3535
city = models.CharField(max_length=20)
3636
state = models.CharField(max_length=2)
37+
zip_code = models.IntegerField(db_index=True)
3738

3839

3940
class Author(models.Model):

tests/schema_/__init__.py

Whitespace-only changes.

tests/schema_/models.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from django.apps.registry import Apps
2+
from django.db import models
3+
4+
from django_mongodb.fields import EmbeddedModelField
5+
6+
# Because we want to test creation and deletion of these as separate things,
7+
# these models are all inserted into a separate Apps so the main test
8+
# runner doesn't migrate them.
9+
10+
new_apps = Apps()
11+
12+
13+
class Address(models.Model):
14+
city = models.CharField(max_length=20)
15+
state = models.CharField(max_length=2)
16+
zip_code = models.IntegerField(db_index=True)
17+
uid = models.IntegerField(unique=True)
18+
unique_together_one = models.CharField(max_length=10)
19+
unique_together_two = models.CharField(max_length=10)
20+
21+
class Meta:
22+
apps = new_apps
23+
unique_together = [("unique_together_one", "unique_together_two")]
24+
25+
26+
class Author(models.Model):
27+
name = models.CharField(max_length=10)
28+
age = models.IntegerField(db_index=True)
29+
address = EmbeddedModelField(Address)
30+
employee_id = models.IntegerField(unique=True)
31+
unique_together_three = models.CharField(max_length=10)
32+
unique_together_four = models.CharField(max_length=10)
33+
34+
class Meta:
35+
apps = new_apps
36+
unique_together = [("unique_together_three", "unique_together_four")]
37+
38+
39+
class Book(models.Model):
40+
name = models.CharField(max_length=100)
41+
author = EmbeddedModelField(Author)
42+
43+
class Meta:
44+
apps = new_apps

0 commit comments

Comments
 (0)