Skip to content

Commit 2b5ae54

Browse files
committed
prevent the creation of embedded models
1 parent 11754df commit 2b5ae54

File tree

14 files changed

+164
-14
lines changed

14 files changed

+164
-14
lines changed

django_mongodb_backend/fields/embedded_model.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,18 @@ def __init__(self, embedded_model, *args, **kwargs):
1818
super().__init__(*args, **kwargs)
1919

2020
def check(self, **kwargs):
21+
from ..models import EmbeddedModel
22+
2123
errors = super().check(**kwargs)
24+
if not issubclass(self.embedded_model, EmbeddedModel):
25+
return [
26+
checks.Error(
27+
"Embedded model must be a subclass of "
28+
"django_mongodb_backend.models.EmbeddedModel.",
29+
obj=self,
30+
id="django_mongodb.embedded_model.E002",
31+
)
32+
]
2233
for field in self.embedded_model._meta.fields:
2334
if field.remote_field:
2435
errors.append(

django_mongodb_backend/managers.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,43 @@
1+
from django.db import NotSupportedError
12
from django.db.models.manager import BaseManager
23

34
from .queryset import MongoQuerySet
45

56

67
class MongoManager(BaseManager.from_queryset(MongoQuerySet)):
78
pass
9+
10+
11+
class NoWriteManager(BaseManager):
12+
def get_queryset(self):
13+
raise NotSupportedError("An EmbeddedModel cannot be queried.")
14+
15+
def all(self):
16+
raise NotSupportedError("An EmbeddedModel cannot be queried.")
17+
18+
def get(self, *args, **kwargs):
19+
raise NotSupportedError("An EmbeddedModel cannot be queried.")
20+
21+
def get_or_create(self, **kwargs):
22+
raise NotSupportedError("An EmbeddedModel cannot be queried.")
23+
24+
def filter(self, *args, **kwargs):
25+
raise NotSupportedError("An EmbeddedModel cannot be queried.")
26+
27+
def create(self, **kwargs):
28+
raise NotSupportedError("An EmbeddedModel cannot be created.")
29+
30+
def bulk_create(self, *args, **kwargs):
31+
raise NotSupportedError("An EmbeddedModel cannot be created.")
32+
33+
def update(self, *args, **kwargs):
34+
raise NotSupportedError("An EmbeddedModel cannot be updated.")
35+
36+
def bulk_update(self, *args, **kwargs):
37+
raise NotSupportedError("An EmbeddedModel cannot be updated.")
38+
39+
def update_or_create(self, **kwargs):
40+
raise NotSupportedError("An EmbeddedModel cannot be updated or created.")
41+
42+
def delete(self):
43+
raise NotSupportedError("An EmbeddedModel cannot be deleted.")

django_mongodb_backend/models.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from django.db import NotSupportedError, models
2+
3+
from .managers import NoWriteManager
4+
5+
6+
class EmbeddedModel(models.Model):
7+
objects = NoWriteManager()
8+
9+
class Meta:
10+
abstract = True
11+
12+
def delete(self, *args, **kwargs):
13+
raise NotSupportedError("An EmbeddedModel cannot be deleted.")
14+
15+
def save(self, *args, **kwargs):
16+
raise NotSupportedError("An EmbeddedModel cannot be saved.")

django_mongodb_backend/schema.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ def get_database(self):
2323

2424
@wrap_database_errors
2525
def create_model(self, model):
26+
from .models import EmbeddedModel
27+
28+
if issubclass(model, EmbeddedModel):
29+
return
2630
self.get_database().create_collection(model._meta.db_table)
2731
self._create_model_indexes(model)
2832
# Make implicit M2M tables.
@@ -76,6 +80,10 @@ def _create_model_indexes(self, model, column_prefix="", parent_model=None):
7680
self.add_index(model, index, column_prefix=column_prefix, parent_model=parent_model)
7781

7882
def delete_model(self, model):
83+
from .models import EmbeddedModel
84+
85+
if issubclass(model, EmbeddedModel):
86+
return
7987
# Delete implicit M2m tables.
8088
for field in model._meta.local_many_to_many:
8189
if field.remote_field.through._meta.auto_created:

docs/source/embedded-models.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ The basics
1111
Let's consider this example::
1212

1313
from django_mongodb_backend.fields import EmbeddedModelField
14+
from django_mongodb_backend.models import EmbeddedModel
1415

1516
class Customer(models.Model):
1617
name = models.CharField(...)
1718
address = EmbeddedModelField("Address")
1819
...
1920

20-
class Address(models.Model):
21+
class Address(EmbeddedModel):
2122
...
2223
city = models.CharField(...)
2324

docs/source/fields.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,8 @@ Stores a model of type ``embedded_model``.
224224

225225
Specifies the model class to embed. It can be either a concrete model
226226
class or a :ref:`lazy reference <lazy-relationships>` to a model class.
227+
The target model must be a subclass of
228+
``django_mongodb_backend.models.EmbeddedModel``.
227229

228230
The embedded model cannot have relational fields
229231
(:class:`~django.db.models.ForeignKey`,
@@ -234,11 +236,12 @@ Stores a model of type ``embedded_model``.
234236

235237
from django.db import models
236238
from django_mongodb_backend.fields import EmbeddedModelField
239+
from django_mongodb_backend.models import EmbeddedModel
237240

238-
class Address(models.Model):
241+
class Address(EmbeddedModel):
239242
...
240243

241-
class Author(models.Model):
244+
class Author(EmbeddedModel):
242245
address = EmbeddedModelField(Address)
243246

244247
class Book(models.Model):

docs/source/models.rst

Whitespace-only changes.

tests/model_fields_/models.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from django.db import models
44

55
from django_mongodb_backend.fields import ArrayField, EmbeddedModelField, ObjectIdField
6+
from django_mongodb_backend.models import EmbeddedModel
67

78

89
# ObjectIdField
@@ -98,19 +99,19 @@ class Holder(models.Model):
9899
data = EmbeddedModelField("Data", null=True, blank=True)
99100

100101

101-
class Data(models.Model):
102+
class Data(EmbeddedModel):
102103
integer = models.IntegerField(db_column="custom_column")
103104
auto_now = models.DateTimeField(auto_now=True)
104105
auto_now_add = models.DateTimeField(auto_now_add=True)
105106

106107

107-
class Address(models.Model):
108+
class Address(EmbeddedModel):
108109
city = models.CharField(max_length=20)
109110
state = models.CharField(max_length=2)
110111
zip_code = models.IntegerField(db_index=True)
111112

112113

113-
class Author(models.Model):
114+
class Author(EmbeddedModel):
114115
name = models.CharField(max_length=10)
115116
age = models.IntegerField()
116117
address = EmbeddedModelField(Address)

tests/model_fields_/test_embedded_model.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from django.test.utils import isolate_apps
55

66
from django_mongodb_backend.fields import EmbeddedModelField
7+
from django_mongodb_backend.models import EmbeddedModel
78

89
from .models import (
910
Address,
@@ -108,7 +109,7 @@ def test_nested(self):
108109
@isolate_apps("model_fields_")
109110
class CheckTests(SimpleTestCase):
110111
def test_no_relational_fields(self):
111-
class Target(models.Model):
112+
class Target(EmbeddedModel):
112113
key = models.ForeignKey("MyModel", models.CASCADE)
113114

114115
class MyModel(models.Model):
@@ -123,3 +124,20 @@ class MyModel(models.Model):
123124
self.assertEqual(
124125
msg, "Embedded models cannot have relational fields (Target.key is a ForeignKey)."
125126
)
127+
128+
def test_embedded_model_subclass(self):
129+
class Target(models.Model):
130+
pass
131+
132+
class MyModel(models.Model):
133+
field = EmbeddedModelField(Target)
134+
135+
model = MyModel()
136+
errors = model.check()
137+
self.assertEqual(len(errors), 1)
138+
# The inner CharField has a non-positive max_length.
139+
self.assertEqual(errors[0].id, "django_mongodb.embedded_model.E002")
140+
msg = errors[0].msg
141+
self.assertEqual(
142+
msg, "Embedded model must be a subclass of django_mongodb_backend.models.EmbeddedModel."
143+
)

tests/models_/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)