Fixed detection of unique=True in embedded documents.
Added some more test cases - thanks to @heyman for the initial test case. Closes #172 Refs #171
This commit is contained in:
parent
04953351f1
commit
1b72ea9cc1
@ -306,6 +306,30 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
|
|||||||
for spec in meta['indexes']] + base_indexes
|
for spec in meta['indexes']] + base_indexes
|
||||||
new_class._meta['indexes'] = user_indexes
|
new_class._meta['indexes'] = user_indexes
|
||||||
|
|
||||||
|
unique_indexes = cls._unique_with_indexes(new_class)
|
||||||
|
new_class._meta['unique_indexes'] = unique_indexes
|
||||||
|
|
||||||
|
for field_name, field in new_class._fields.items():
|
||||||
|
# Check for custom primary key
|
||||||
|
if field.primary_key:
|
||||||
|
current_pk = new_class._meta['id_field']
|
||||||
|
if current_pk and current_pk != field_name:
|
||||||
|
raise ValueError('Cannot override primary key field')
|
||||||
|
|
||||||
|
if not current_pk:
|
||||||
|
new_class._meta['id_field'] = field_name
|
||||||
|
# Make 'Document.id' an alias to the real primary key field
|
||||||
|
new_class.id = field
|
||||||
|
|
||||||
|
if not new_class._meta['id_field']:
|
||||||
|
new_class._meta['id_field'] = 'id'
|
||||||
|
new_class._fields['id'] = ObjectIdField(db_field='_id')
|
||||||
|
new_class.id = new_class._fields['id']
|
||||||
|
|
||||||
|
return new_class
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _unique_with_indexes(cls, new_class, namespace=""):
|
||||||
unique_indexes = []
|
unique_indexes = []
|
||||||
for field_name, field in new_class._fields.items():
|
for field_name, field in new_class._fields.items():
|
||||||
# Generate a list of indexes needed by uniqueness constraints
|
# Generate a list of indexes needed by uniqueness constraints
|
||||||
@ -331,28 +355,16 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
|
|||||||
unique_fields += unique_with
|
unique_fields += unique_with
|
||||||
|
|
||||||
# Add the new index to the list
|
# Add the new index to the list
|
||||||
index = [(f, pymongo.ASCENDING) for f in unique_fields]
|
index = [("%s%s" % (namespace, f), pymongo.ASCENDING) for f in unique_fields]
|
||||||
unique_indexes.append(index)
|
unique_indexes.append(index)
|
||||||
|
|
||||||
# Check for custom primary key
|
# Grab any embedded document field unique indexes
|
||||||
if field.primary_key:
|
if field.__class__.__name__ == "EmbeddedDocumentField":
|
||||||
current_pk = new_class._meta['id_field']
|
field_namespace = "%s." % field_name
|
||||||
if current_pk and current_pk != field_name:
|
unique_indexes += cls._unique_with_indexes(field.document_type,
|
||||||
raise ValueError('Cannot override primary key field')
|
field_namespace)
|
||||||
|
|
||||||
if not current_pk:
|
return unique_indexes
|
||||||
new_class._meta['id_field'] = field_name
|
|
||||||
# Make 'Document.id' an alias to the real primary key field
|
|
||||||
new_class.id = field
|
|
||||||
|
|
||||||
new_class._meta['unique_indexes'] = unique_indexes
|
|
||||||
|
|
||||||
if not new_class._meta['id_field']:
|
|
||||||
new_class._meta['id_field'] = 'id'
|
|
||||||
new_class._fields['id'] = ObjectIdField(db_field='_id')
|
|
||||||
new_class.id = new_class._fields['id']
|
|
||||||
|
|
||||||
return new_class
|
|
||||||
|
|
||||||
|
|
||||||
class BaseDocument(object):
|
class BaseDocument(object):
|
||||||
|
@ -362,6 +362,10 @@ class DocumentTest(unittest.TestCase):
|
|||||||
post2 = BlogPost(title='test2', slug='test')
|
post2 = BlogPost(title='test2', slug='test')
|
||||||
self.assertRaises(OperationError, post2.save)
|
self.assertRaises(OperationError, post2.save)
|
||||||
|
|
||||||
|
|
||||||
|
def test_unique_with(self):
|
||||||
|
"""Ensure that unique_with constraints are applied to fields.
|
||||||
|
"""
|
||||||
class Date(EmbeddedDocument):
|
class Date(EmbeddedDocument):
|
||||||
year = IntField(db_field='yr')
|
year = IntField(db_field='yr')
|
||||||
|
|
||||||
@ -385,6 +389,63 @@ class DocumentTest(unittest.TestCase):
|
|||||||
|
|
||||||
BlogPost.drop_collection()
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
|
def test_unique_embedded_document(self):
|
||||||
|
"""Ensure that uniqueness constraints are applied to fields on embedded documents.
|
||||||
|
"""
|
||||||
|
class SubDocument(EmbeddedDocument):
|
||||||
|
year = IntField(db_field='yr')
|
||||||
|
slug = StringField(unique=True)
|
||||||
|
|
||||||
|
class BlogPost(Document):
|
||||||
|
title = StringField()
|
||||||
|
sub = EmbeddedDocumentField(SubDocument)
|
||||||
|
|
||||||
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
|
post1 = BlogPost(title='test1', sub=SubDocument(year=2009, slug="test"))
|
||||||
|
post1.save()
|
||||||
|
|
||||||
|
# sub.slug is different so won't raise exception
|
||||||
|
post2 = BlogPost(title='test2', sub=SubDocument(year=2010, slug='another-slug'))
|
||||||
|
post2.save()
|
||||||
|
|
||||||
|
# Now there will be two docs with the same sub.slug
|
||||||
|
post3 = BlogPost(title='test3', sub=SubDocument(year=2010, slug='test'))
|
||||||
|
self.assertRaises(OperationError, post3.save)
|
||||||
|
|
||||||
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
|
def test_unique_with_embedded_document_and_embedded_unique(self):
|
||||||
|
"""Ensure that uniqueness constraints are applied to fields on
|
||||||
|
embedded documents. And work with unique_with as well.
|
||||||
|
"""
|
||||||
|
class SubDocument(EmbeddedDocument):
|
||||||
|
year = IntField(db_field='yr')
|
||||||
|
slug = StringField(unique=True)
|
||||||
|
|
||||||
|
class BlogPost(Document):
|
||||||
|
title = StringField(unique_with='sub.year')
|
||||||
|
sub = EmbeddedDocumentField(SubDocument)
|
||||||
|
|
||||||
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
|
post1 = BlogPost(title='test1', sub=SubDocument(year=2009, slug="test"))
|
||||||
|
post1.save()
|
||||||
|
|
||||||
|
# sub.slug is different so won't raise exception
|
||||||
|
post2 = BlogPost(title='test2', sub=SubDocument(year=2010, slug='another-slug'))
|
||||||
|
post2.save()
|
||||||
|
|
||||||
|
# Now there will be two docs with the same sub.slug
|
||||||
|
post3 = BlogPost(title='test3', sub=SubDocument(year=2010, slug='test'))
|
||||||
|
self.assertRaises(OperationError, post3.save)
|
||||||
|
|
||||||
|
# Now there will be two docs with the same title and year
|
||||||
|
post3 = BlogPost(title='test1', sub=SubDocument(year=2009, slug='test-1'))
|
||||||
|
self.assertRaises(OperationError, post3.save)
|
||||||
|
|
||||||
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
def test_unique_and_indexes(self):
|
def test_unique_and_indexes(self):
|
||||||
"""Ensure that 'unique' constraints aren't overridden by
|
"""Ensure that 'unique' constraints aren't overridden by
|
||||||
meta.indexes.
|
meta.indexes.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user