Skip to content

Commit 4d3ab60

Browse files
authored
Merge pull request #2863 from bagerard/improve_error_message_unsaved_object
Error improvement for referenced Documents not saved yet
2 parents 64aab4d + 8e855c8 commit 4d3ab60

File tree

3 files changed

+30
-38
lines changed

3 files changed

+30
-38
lines changed

docs/changelog.rst

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Development
2222
- Add support for collation/hint/comment to delete/update and aggregate #2842
2323
- BREAKING CHANGE: Remove LongField as it's equivalent to IntField since we drop support to Python2 long time ago (User should simply switch to IntField) #2309
2424
- BugFix - Calling .clear on a ListField wasn't being marked as changed (and flushed to db upon .save()) #2858
25+
- Improve error message in case a document assigned to a ReferenceField wasn't saved yet #1955
2526
- BugFix - Take `where()` into account when using `.modify()`, as in MyDocument.objects().where("this[field] >= this[otherfield]").modify(field='new') #2044
2627

2728
Changes in 0.29.0

mongoengine/fields.py

+19-34
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,14 @@
111111
RECURSIVE_REFERENCE_CONSTANT = "self"
112112

113113

114+
def _unsaved_object_error(document):
115+
return (
116+
f"The instance of the document '{document}' you are "
117+
"trying to reference has an empty 'id'. You can only reference "
118+
"documents once they have been saved to the database"
119+
)
120+
121+
114122
class StringField(BaseField):
115123
"""A unicode string field."""
116124

@@ -1215,10 +1223,7 @@ def to_mongo(self, document):
12151223

12161224
# XXX ValidationError raised outside of the "validate" method.
12171225
if id_ is None:
1218-
self.error(
1219-
"You can only reference documents once they have"
1220-
" been saved to the database"
1221-
)
1226+
self.error(_unsaved_object_error(document.__class__.__name__))
12221227

12231228
# Use the attributes from the document instance, so that they
12241229
# override the attributes of this field's document type
@@ -1262,10 +1267,7 @@ def validate(self, value):
12621267
)
12631268

12641269
if isinstance(value, Document) and value.id is None:
1265-
self.error(
1266-
"You can only reference documents once they have been "
1267-
"saved to the database"
1268-
)
1270+
self.error(_unsaved_object_error(value.__class__.__name__))
12691271

12701272
def lookup_member(self, member_name):
12711273
return self.document_type._fields.get(member_name)
@@ -1370,10 +1372,7 @@ def to_mongo(self, document, use_db_field=True, fields=None):
13701372
# We need the id from the saved object to create the DBRef
13711373
id_ = document.pk
13721374
if id_ is None:
1373-
self.error(
1374-
"You can only reference documents once they have"
1375-
" been saved to the database"
1376-
)
1375+
self.error(_unsaved_object_error(document.__class__.__name__))
13771376
else:
13781377
self.error("Only accept a document object")
13791378

@@ -1394,10 +1393,7 @@ def prepare_query_value(self, op, value):
13941393
# XXX ValidationError raised outside of the "validate" method.
13951394
if isinstance(value, Document):
13961395
if value.pk is None:
1397-
self.error(
1398-
"You can only reference documents once they have"
1399-
" been saved to the database"
1400-
)
1396+
self.error(_unsaved_object_error(value.__class__.__name__))
14011397
value_dict = {"_id": value.pk}
14021398
for field in self.fields:
14031399
value_dict.update({field: value[field]})
@@ -1411,10 +1407,7 @@ def validate(self, value):
14111407
self.error("A CachedReferenceField only accepts documents")
14121408

14131409
if isinstance(value, Document) and value.id is None:
1414-
self.error(
1415-
"You can only reference documents once they have been "
1416-
"saved to the database"
1417-
)
1410+
self.error(_unsaved_object_error(value.__class__.__name__))
14181411

14191412
def lookup_member(self, member_name):
14201413
return self.document_type._fields.get(member_name)
@@ -1513,10 +1506,7 @@ def validate(self, value):
15131506

15141507
# We need the id from the saved object to create the DBRef
15151508
elif isinstance(value, Document) and value.id is None:
1516-
self.error(
1517-
"You can only reference documents once they have been"
1518-
" saved to the database"
1519-
)
1509+
self.error(_unsaved_object_error(value.__class__.__name__))
15201510

15211511
def to_mongo(self, document):
15221512
if document is None:
@@ -1533,10 +1523,7 @@ def to_mongo(self, document):
15331523
id_ = document.id
15341524
if id_ is None:
15351525
# XXX ValidationError raised outside of the "validate" method.
1536-
self.error(
1537-
"You can only reference documents once they have"
1538-
" been saved to the database"
1539-
)
1526+
self.error(_unsaved_object_error(document.__class__.__name__))
15401527
else:
15411528
id_ = document
15421529

@@ -2535,10 +2522,7 @@ def validate(self, value):
25352522
)
25362523

25372524
if pk is None:
2538-
self.error(
2539-
"You can only reference documents once they have been "
2540-
"saved to the database"
2541-
)
2525+
self.error(_unsaved_object_error(self.document_type.__name__))
25422526

25432527
def prepare_query_value(self, op, value):
25442528
if value is None:
@@ -2607,8 +2591,9 @@ def __get__(self, instance, owner):
26072591
def validate(self, value):
26082592
if isinstance(value, LazyReference) and value.pk is None:
26092593
self.error(
2610-
"You can only reference documents once they have been"
2611-
" saved to the database"
2594+
_unsaved_object_error(
2595+
self.__class__.__name__
2596+
) # Actual class is difficult to predict here
26122597
)
26132598
return super().validate(value)
26142599

tests/fields/test_reference_field.py

+10-4
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class Test(Document):
2020
class NonDocumentSubClass:
2121
pass
2222

23-
# fails if given an non Document subclass
23+
# fails if given a non Document subclass
2424
with pytest.raises(ValidationError, match=ERROR_MSG):
2525

2626
class Test(Document): # noqa: F811
@@ -46,12 +46,17 @@ class BlogPost(Document):
4646
with pytest.raises(ValidationError):
4747
ReferenceField(EmbeddedDocument)
4848

49-
user = User(name="Test User")
49+
unsaved_user = User(name="Test User")
5050

5151
# Ensure that the referenced object must have been saved
5252
post1 = BlogPost(content="Chips and gravy taste good.")
53-
post1.author = user
54-
with pytest.raises(ValidationError):
53+
post1.author = unsaved_user
54+
expected_error = (
55+
"The instance of the document 'User' you are "
56+
"trying to reference has an empty 'id'. You can only reference "
57+
"documents once they have been saved to the database"
58+
)
59+
with pytest.raises(ValidationError, match=expected_error):
5560
post1.save()
5661

5762
# Check that an invalid object type cannot be used
@@ -61,6 +66,7 @@ class BlogPost(Document):
6166
post1.validate()
6267

6368
# Ensure ObjectID's are accepted as references
69+
user = User(name="Test User")
6470
user_object_id = user.pk
6571
post3 = BlogPost(content="Chips and curry sauce taste good.")
6672
post3.author = user_object_id

0 commit comments

Comments
 (0)