new save() method updates only dirty fields. fixes issue #18
This commit is contained in:
parent
7d6e117f68
commit
431f006751
@ -15,13 +15,14 @@ class BaseField(object):
|
|||||||
_index_with_types = True
|
_index_with_types = True
|
||||||
|
|
||||||
def __init__(self, name=None, required=False, default=None, unique=False,
|
def __init__(self, name=None, required=False, default=None, unique=False,
|
||||||
unique_with=None, primary_key=False):
|
unique_with=None, primary_key=False, modified=False):
|
||||||
self.name = name if not primary_key else '_id'
|
self.name = name if not primary_key else '_id'
|
||||||
self.required = required or primary_key
|
self.required = required or primary_key
|
||||||
self.default = default
|
self.default = default
|
||||||
self.unique = bool(unique or unique_with)
|
self.unique = bool(unique or unique_with)
|
||||||
self.unique_with = unique_with
|
self.unique_with = unique_with
|
||||||
self.primary_key = primary_key
|
self.primary_key = primary_key
|
||||||
|
self.modified = modified
|
||||||
|
|
||||||
def __get__(self, instance, owner):
|
def __get__(self, instance, owner):
|
||||||
"""Descriptor for retrieving a value from a field in a document. Do
|
"""Descriptor for retrieving a value from a field in a document. Do
|
||||||
@ -44,6 +45,7 @@ class BaseField(object):
|
|||||||
"""Descriptor for assigning a value to a field in a document.
|
"""Descriptor for assigning a value to a field in a document.
|
||||||
"""
|
"""
|
||||||
instance._data[self.name] = value
|
instance._data[self.name] = value
|
||||||
|
self.modified = True
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
"""Convert a MongoDB-compatible type to a Python type.
|
"""Convert a MongoDB-compatible type to a Python type.
|
||||||
@ -252,8 +254,11 @@ class BaseDocument(object):
|
|||||||
|
|
||||||
def __init__(self, **values):
|
def __init__(self, **values):
|
||||||
self._data = {}
|
self._data = {}
|
||||||
|
|
||||||
|
modified = 'id' in values.keys()
|
||||||
# Assign initial values to instance
|
# Assign initial values to instance
|
||||||
for attr_name, attr_value in self._fields.items():
|
for attr_name, attr_value in self._fields.items():
|
||||||
|
attr_value.modified = modified
|
||||||
if attr_name in values:
|
if attr_name in values:
|
||||||
setattr(self, attr_name, values.pop(attr_name))
|
setattr(self, attr_name, values.pop(attr_name))
|
||||||
else:
|
else:
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
from base import (DocumentMetaclass, TopLevelDocumentMetaclass, BaseDocument,
|
from base import (DocumentMetaclass, TopLevelDocumentMetaclass, BaseDocument,
|
||||||
ValidationError)
|
ValidationError)
|
||||||
from queryset import OperationError
|
from queryset import OperationError, QuerySet
|
||||||
from connection import _get_db
|
from connection import _get_db
|
||||||
|
|
||||||
|
|
||||||
import pymongo
|
import pymongo
|
||||||
|
|
||||||
|
|
||||||
@ -75,12 +76,30 @@ class Document(BaseDocument):
|
|||||||
if force_insert:
|
if force_insert:
|
||||||
object_id = collection.insert(doc, safe=safe)
|
object_id = collection.insert(doc, safe=safe)
|
||||||
else:
|
else:
|
||||||
object_id = collection.save(doc, safe=safe)
|
if getattr(self, 'id', None) == None:
|
||||||
|
# new document
|
||||||
|
object_id = collection.save(doc, safe=safe)
|
||||||
|
else:
|
||||||
|
# update document
|
||||||
|
modified_fields = map(lambda obj: obj[0], filter(lambda obj: obj[1].modified, self._fields.items()))
|
||||||
|
modified_doc = dict(filter(lambda k: k[0] in modified_fields, doc.items()))
|
||||||
|
try:
|
||||||
|
id_field = self._meta['id_field']
|
||||||
|
idObj = self._fields[id_field].to_mongo(self['id'])
|
||||||
|
collection.update({'_id': idObj}, {'$set': modified_doc}, safe=safe)
|
||||||
|
except pymongo.errors.OperationFailure, err:
|
||||||
|
if str(err) == 'multi not coded yet':
|
||||||
|
raise OperationError('update() method requires MongoDB 1.1.3+')
|
||||||
|
raise OperationError('Update failed (%s)' % str(err))
|
||||||
|
object_id = self['id']
|
||||||
|
|
||||||
|
for field in self._fields.values(): field.modified = False
|
||||||
except pymongo.errors.OperationFailure, err:
|
except pymongo.errors.OperationFailure, err:
|
||||||
message = 'Could not save document (%s)'
|
message = 'Could not save document (%s)'
|
||||||
if 'duplicate key' in str(err):
|
if 'duplicate key' in str(err):
|
||||||
message = 'Tried to save duplicate unique keys (%s)'
|
message = 'Tried to save duplicate unique keys (%s)'
|
||||||
raise OperationError(message % str(err))
|
raise OperationError(message % str(err))
|
||||||
|
|
||||||
id_field = self._meta['id_field']
|
id_field = self._meta['id_field']
|
||||||
self[id_field] = self._fields[id_field].to_python(object_id)
|
self[id_field] = self._fields[id_field].to_python(object_id)
|
||||||
|
|
||||||
@ -106,6 +125,7 @@ class Document(BaseDocument):
|
|||||||
obj = self.__class__.objects(**{id_field: self[id_field]}).first()
|
obj = self.__class__.objects(**{id_field: self[id_field]}).first()
|
||||||
for field in self._fields:
|
for field in self._fields:
|
||||||
setattr(self, field, obj[field])
|
setattr(self, field, obj[field])
|
||||||
|
obj.modified = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def drop_collection(cls):
|
def drop_collection(cls):
|
||||||
|
@ -71,6 +71,7 @@ class FloatField(BaseField):
|
|||||||
return float(value)
|
return float(value)
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
|
if isinstance(value, int): value = float(value)
|
||||||
assert isinstance(value, float)
|
assert isinstance(value, float)
|
||||||
|
|
||||||
if self.min_value is not None and value < self.min_value:
|
if self.min_value is not None and value < self.min_value:
|
||||||
|
@ -316,15 +316,17 @@ class QuerySet(object):
|
|||||||
elif cnt == 1:
|
elif cnt == 1:
|
||||||
return dataset.first()
|
return dataset.first()
|
||||||
else:
|
else:
|
||||||
raise MultipleObjectsReturned(u'%d items returned, instead of 1' % cnt)
|
raise MultipleObjectsReturned(u'%d items returned, expected exactly one' % cnt)
|
||||||
|
|
||||||
def get(self, **kwargs):
|
def get(self, **kwargs):
|
||||||
|
"""Retreive exactly one document. Raise DoesNotExist if it's not found.
|
||||||
|
"""
|
||||||
dataset = self.filter(**kwargs)
|
dataset = self.filter(**kwargs)
|
||||||
cnt = dataset.count()
|
cnt = dataset.count()
|
||||||
if cnt == 1:
|
if cnt == 1:
|
||||||
return dataset.first()
|
return dataset.first()
|
||||||
elif cnt > 1:
|
elif cnt > 1:
|
||||||
raise MultipleObjectsReturned(u'%d items returned, instead of 1' % cnt)
|
raise MultipleObjectsReturned(u'%d items returned, expected exactly one' % cnt)
|
||||||
else:
|
else:
|
||||||
raise DoesNotExist('Document not found')
|
raise DoesNotExist('Document not found')
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user