Tweaked SequenceField so that it doesn't increment on creation.

[refs #238]
This commit is contained in:
Ross Lawley 2011-07-19 16:51:26 +01:00
parent 5eb895b952
commit 5834fa840c
5 changed files with 95 additions and 31 deletions

View File

@ -4,3 +4,4 @@ Deepak Thukral <iapain@yahoo.com>
Florian Schlachter <flori@n-schlachter.de> Florian Schlachter <flori@n-schlachter.de>
Steve Challis <steve@stevechallis.com> Steve Challis <steve@stevechallis.com>
Ross Lawley <ross.lawley@gmail.com> Ross Lawley <ross.lawley@gmail.com>
Wilson Júnior <wilsonpjunior@gmail.com>

View File

@ -5,6 +5,7 @@ Changelog
Changes in dev Changes in dev
============== ==============
- Added SequenceField - for creating sequential counters
- Added update() convenience method to a document - Added update() convenience method to a document
- Added cascading saves - so changes to Referenced documents are saved on .save() - Added cascading saves - so changes to Referenced documents are saved on .save()
- Added select_related() support - Added select_related() support

View File

@ -587,7 +587,8 @@ class BaseDocument(object):
# Set any get_fieldname_display methods # Set any get_fieldname_display methods
self.__set_field_display() self.__set_field_display()
# Flag initialised
self._initialised = True
signals.post_init.send(self.__class__, document=self) signals.post_init.send(self.__class__, document=self)
def validate(self): def validate(self):

View File

@ -880,30 +880,46 @@ class GeoPointField(BaseField):
class SequenceField(IntField): class SequenceField(IntField):
"""Provides a sequental counter.
..note:: Although traditional databases often use increasing sequence
numbers for primary keys. In MongoDB, the preferred approach is to
use Object IDs instead. The concept is that in a very large
cluster of machines, it is easier to create an object ID than have
global, uniformly increasing sequence numbers.
.. versionadded:: 0.5
"""
def __init__(self, collection_name=None, *args, **kwargs):
self.collection_name = collection_name or 'mongoengine.counters'
return super(SequenceField, self).__init__(*args, **kwargs)
def generate_new_value(self): def generate_new_value(self):
""" """
Generate and Increment counter Generate and Increment the counter
""" """
sequence_id = "{0}.{1}".format(self.owner_document._get_collection_name(), sequence_id = "{0}.{1}".format(self.owner_document._get_collection_name(),
self.name) self.name)
collection = _get_db()['mongoengine.counters'] collection = _get_db()[self.collection_name]
counter = collection.find_and_modify(query={"_id": sequence_id}, counter = collection.find_and_modify(query={"_id": sequence_id},
update={"$inc" : {"next": 1}}, update={"$inc": {"next": 1}},
new=True, new=True,
upsert=True) upsert=True)
return counter['next'] return counter['next']
def __get__(self, instance, owner): def __get__(self, instance, owner):
if not instance._data:
return
if instance is None: if instance is None:
return self return self
if not instance._data:
return
value = instance._data.get(self.name) value = instance._data.get(self.name)
if not value and instance._initialised:
if not value:
value = self.generate_new_value() value = self.generate_new_value()
instance._data[self.name] = value instance._data[self.name] = value
return value
def to_python(self, value):
if value is None:
value = self.generate_new_value()
return value return value

View File

@ -1379,20 +1379,7 @@ class FieldTest(unittest.TestCase):
self.assertEqual(d2.data, {}) self.assertEqual(d2.data, {})
self.assertEqual(d2.data2, {}) self.assertEqual(d2.data2, {})
def test_sequence_field(self): def test_sequence_field(self):
class Person(Document):
id = SequenceField(primary_key=True)
self.db['mongoengine.counters'].drop()
Person.drop_collection()
p = Person()
p.save()
p = Person.objects.first()
self.assertEqual(p.id, 1)
def test_multiple_sequence_field(self):
class Person(Document): class Person(Document):
id = SequenceField(primary_key=True) id = SequenceField(primary_key=True)
name = StringField() name = StringField()
@ -1404,18 +1391,76 @@ class FieldTest(unittest.TestCase):
p = Person(name="Person %s" % x) p = Person(name="Person %s" % x)
p.save() p.save()
c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'})
self.assertEqual(c['next'], 10)
ids = [i.id for i in Person.objects] ids = [i.id for i in Person.objects]
self.assertEqual(ids, range(1, 11)) self.assertEqual(ids, range(1, 11))
c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'})
self.assertEqual(c['next'], 10)
def test_multiple_sequence_fields(self):
class Person(Document):
id = SequenceField(primary_key=True)
counter = SequenceField()
name = StringField()
self.db['mongoengine.counters'].drop()
Person.drop_collection()
for x in xrange(10): for x in xrange(10):
p = Person(name="Person %s" % x) p = Person(name="Person %s" % x)
p.save() p.save()
ids = [i.id for i in Person.objects] c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'})
self.assertEqual(ids, range(1, 21)) self.assertEqual(c['next'], 10)
ids = [i.id for i in Person.objects]
self.assertEqual(ids, range(1, 11))
counters = [i.counter for i in Person.objects]
self.assertEqual(counters, range(1, 11))
c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'})
self.assertEqual(c['next'], 10)
def test_multiple_sequence_fields_on_docs(self):
class Animal(Document):
id = SequenceField(primary_key=True)
class Person(Document):
id = SequenceField(primary_key=True)
self.db['mongoengine.counters'].drop()
Animal.drop_collection()
Person.drop_collection()
for x in xrange(10):
a = Animal(name="Animal %s" % x)
a.save()
p = Person(name="Person %s" % x)
p.save()
c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'})
self.assertEqual(c['next'], 10)
c = self.db['mongoengine.counters'].find_one({'_id': 'animal.id'})
self.assertEqual(c['next'], 10)
ids = [i.id for i in Person.objects]
self.assertEqual(ids, range(1, 11))
id = [i.id for i in Animal.objects]
self.assertEqual(id, range(1, 11))
c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'})
self.assertEqual(c['next'], 10)
c = self.db['mongoengine.counters'].find_one({'_id': 'animal.id'})
self.assertEqual(c['next'], 10)
counter = self.db['mongoengine.counters'].find_one({'_id': 'person.id'})
self.assertEqual(counter['next'], 20)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()