From 99fe1da34564b29cb2505fe938b09ca4253a884c Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Wed, 7 Nov 2012 13:20:34 +0000 Subject: [PATCH] Add value_decorator into SequenceField Allows post processing of the calculated counter value. --- docs/changelog.rst | 1 + docs/upgrade.rst | 7 +++++++ mongoengine/fields.py | 33 ++++++++++++++++++--------------- tests/test_fields.py | 24 ++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 15 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index ca18d3e9..550cc8dc 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,7 @@ Changelog Changes in 0.8 ============== +- Updated SequenceFields to allow post processing of the calculated counter value (MongoEngine/mongoengine#141) - Added clean method to documents for pre validation data cleaning (MongoEngine/mongoengine#60) - Added support setting for read prefrence at a query level (MongoEngine/mongoengine#157) - Added _instance to EmbeddedDocuments pointing to the parent (MongoEngine/mongoengine#139) diff --git a/docs/upgrade.rst b/docs/upgrade.rst index bf0a8421..daf09126 100644 --- a/docs/upgrade.rst +++ b/docs/upgrade.rst @@ -57,6 +57,13 @@ you will need to declare :attr:`allow_inheritance` in the meta data like so: meta = {'allow_inheritance': True} +SequenceFields +-------------- + +:class:`~mongoengine.fields.SequenceField`s now inherit from `BaseField` to +allow flexible storage of the calculated value. As such MIN and MAX settings +are no longer handled. + 0.6 to 0.7 ========== diff --git a/mongoengine/fields.py b/mongoengine/fields.py index 8aa7f641..e2ce33cd 100644 --- a/mongoengine/fields.py +++ b/mongoengine/fields.py @@ -1329,7 +1329,7 @@ class GeoPointField(BaseField): self.error('Both values in point must be float or int') -class SequenceField(IntField): +class SequenceField(BaseField): """Provides a sequental counter see: http://www.mongodb.org/display/DOCS/Object+IDs#ObjectIDs-SequenceNumbers @@ -1341,15 +1341,26 @@ class SequenceField(IntField): cluster of machines, it is easier to create an object ID than have global, uniformly increasing sequence numbers. + Use any callable as `value_decorator` to transform calculated counter into + any value suitable for your needs, e.g. string or hexadecimal + representation of the default integer counter value. + .. versionadded:: 0.5 + + .. versionchanged:: 0.8 added `value_decorator` """ + _auto_gen = True + COLLECTION_NAME = 'mongoengine.counters' + VALUE_DECORATOR = int def __init__(self, collection_name=None, db_alias=None, - sequence_name=None, *args, **kwargs): - self.collection_name = collection_name or 'mongoengine.counters' + sequence_name=None, value_decorator=None, *args, **kwargs): + self.collection_name = collection_name or self.COLLECTION_NAME self.db_alias = db_alias or DEFAULT_CONNECTION_NAME self.sequence_name = sequence_name + self.value_decorator = (callable(value_decorator) and + value_decorator or self.VALUE_DECORATOR) return super(SequenceField, self).__init__(*args, **kwargs) def generate(self): @@ -1364,24 +1375,16 @@ class SequenceField(IntField): update={"$inc": {"next": 1}}, new=True, upsert=True) - return counter['next'] + return self.value_decorator(counter['next']) def __get__(self, instance, owner): - - if instance is None: - return self - - if not instance._data: - return - - value = instance._data.get(self.name) - - if not value and instance._initialised: + value = super(SequenceField, self).__get__(instance, owner) + if value is None and instance._initialised: value = self.generate() instance._data[self.name] = value instance._mark_as_changed(self.name) - return int(value) if value else None + return value def __set__(self, instance, value): diff --git a/tests/test_fields.py b/tests/test_fields.py index 1c13a58c..f1a36ed7 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- from __future__ import with_statement +import sys +sys.path[0:0] = [""] + import datetime import os import unittest @@ -2184,6 +2187,27 @@ class FieldTest(unittest.TestCase): c = self.db['mongoengine.counters'].find_one({'_id': 'animal.id'}) self.assertEqual(c['next'], 10) + def test_sequence_field_value_decorator(self): + class Person(Document): + id = SequenceField(primary_key=True, value_decorator=str) + name = StringField() + + self.db['mongoengine.counters'].drop() + Person.drop_collection() + + for x in xrange(10): + p = Person(name="Person %s" % x) + 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] + self.assertEqual(ids, map(str, range(1, 11))) + + c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'}) + self.assertEqual(c['next'], 10) + def test_generic_embedded_document(self): class Car(EmbeddedDocument): name = StringField()