Merge pull request #1128 from iici-gli/master

Fixed: ListField minus index assignment does not work #1119
This commit is contained in:
Jérôme Lafréchoux
2016-09-07 09:29:31 +02:00
committed by GitHub
7 changed files with 130 additions and 55 deletions

View File

@@ -310,7 +310,7 @@ class BaseDocument(object):
data = SON()
data["_id"] = None
data['_cls'] = self._class_name
EmbeddedDocumentField = _import_class("EmbeddedDocumentField")
# only root fields ['test1.a', 'test2'] => ['test1', 'test2']
root_fields = set([f.split('.')[0] for f in fields])
@@ -325,18 +325,20 @@ class BaseDocument(object):
field = self._dynamic_fields.get(field_name)
if value is not None:
if fields:
f_inputs = field.to_mongo.__code__.co_varnames
ex_vars = {}
if fields and 'fields' in f_inputs:
key = '%s.' % field_name
embedded_fields = [
i.replace(key, '') for i in fields
if i.startswith(key)]
else:
embedded_fields = []
ex_vars['fields'] = embedded_fields
value = field.to_mongo(value, use_db_field=use_db_field,
fields=embedded_fields)
if 'use_db_field' in f_inputs:
ex_vars['use_db_field'] = use_db_field
value = field.to_mongo(value, **ex_vars)
# Handle self generating fields
if value is None and field._auto_gen:
@@ -489,7 +491,7 @@ class BaseDocument(object):
# remove lower level changed fields
level = '.'.join(levels[:idx]) + '.'
remove = self._changed_fields.remove
for field in self._changed_fields:
for field in self._changed_fields[:]:
if field.startswith(level):
remove(field)
@@ -604,7 +606,9 @@ class BaseDocument(object):
for p in parts:
if isinstance(d, (ObjectId, DBRef)):
break
elif isinstance(d, list) and p.isdigit():
elif isinstance(d, list) and p.lstrip('-').isdigit():
if p[0] == '-':
p = str(len(d)+int(p))
try:
d = d[int(p)]
except IndexError:
@@ -638,7 +642,9 @@ class BaseDocument(object):
parts = path.split('.')
db_field_name = parts.pop()
for p in parts:
if isinstance(d, list) and p.isdigit():
if isinstance(d, list) and p.lstrip('-').isdigit():
if p[0] == '-':
p = str(len(d)+int(p))
d = d[int(p)]
elif (hasattr(d, '__getattribute__') and
not isinstance(d, dict)):
@@ -706,14 +712,6 @@ class BaseDocument(object):
del data[field.db_field]
except (AttributeError, ValueError), e:
errors_dict[field_name] = e
elif field.default:
default = field.default
if callable(default):
default = default()
if isinstance(default, BaseDocument):
changed_fields.append(field_name)
elif not only_fields or field_name in only_fields:
changed_fields.append(field_name)
if errors_dict:
errors = "\n".join(["%s - %s" % (k, v)

View File

@@ -158,11 +158,24 @@ class BaseField(object):
"""
return value
def to_mongo(self, value, **kwargs):
def to_mongo(self, value):
"""Convert a Python type to a MongoDB-compatible type.
"""
return self.to_python(value)
def _to_mongo_safe_call(self, value, use_db_field=True, fields=None):
"""A helper method to call to_mongo with proper inputs
"""
f_inputs = self.to_mongo.__code__.co_varnames
ex_vars = {}
if 'fields' in f_inputs:
ex_vars['fields'] = fields
if 'use_db_field' in f_inputs:
ex_vars['use_db_field'] = use_db_field
return self.to_mongo(value, **ex_vars)
def prepare_query_value(self, op, value):
"""Prepare a value that is being used in a query for PyMongo.
"""
@@ -324,7 +337,7 @@ class ComplexBaseField(BaseField):
key=operator.itemgetter(0))]
return value_dict
def to_mongo(self, value, **kwargs):
def to_mongo(self, value, use_db_field=True, fields=None):
"""Convert a Python type to a MongoDB-compatible type.
"""
Document = _import_class("Document")
@@ -336,10 +349,9 @@ class ComplexBaseField(BaseField):
if hasattr(value, 'to_mongo'):
if isinstance(value, Document):
return GenericReferenceField().to_mongo(
value, **kwargs)
return GenericReferenceField().to_mongo(value)
cls = value.__class__
val = value.to_mongo(**kwargs)
val = value.to_mongo(use_db_field, fields)
# If it's a document that is not inherited add _cls
if isinstance(value, EmbeddedDocument):
val['_cls'] = cls.__name__
@@ -354,7 +366,7 @@ class ComplexBaseField(BaseField):
return value
if self.field:
value_dict = dict([(key, self.field.to_mongo(item, **kwargs))
value_dict = dict([(key, self.field._to_mongo_safe_call(item, use_db_field, fields))
for key, item in value.iteritems()])
else:
value_dict = {}
@@ -373,20 +385,19 @@ class ComplexBaseField(BaseField):
meta.get('allow_inheritance', ALLOW_INHERITANCE)
is True)
if not allow_inheritance and not self.field:
value_dict[k] = GenericReferenceField().to_mongo(
v, **kwargs)
value_dict[k] = GenericReferenceField().to_mongo(v)
else:
collection = v._get_collection_name()
value_dict[k] = DBRef(collection, v.pk)
elif hasattr(v, 'to_mongo'):
cls = v.__class__
val = v.to_mongo(**kwargs)
val = v.to_mongo(use_db_field, fields)
# If it's a document that is not inherited add _cls
if isinstance(v, (Document, EmbeddedDocument)):
val['_cls'] = cls.__name__
value_dict[k] = val
else:
value_dict[k] = self.to_mongo(v, **kwargs)
value_dict[k] = self.to_mongo(v, use_db_field, fields)
if is_list: # Convert back to a list
return [v for _, v in sorted(value_dict.items(),
@@ -444,7 +455,7 @@ class ObjectIdField(BaseField):
pass
return value
def to_mongo(self, value, **kwargs):
def to_mongo(self, value):
if not isinstance(value, ObjectId):
try:
return ObjectId(unicode(value))
@@ -619,7 +630,7 @@ class GeoJsonBaseField(BaseField):
if errors:
return "Invalid MultiPolygon:\n%s" % ", ".join(errors)
def to_mongo(self, value, **kwargs):
def to_mongo(self, value):
if isinstance(value, dict):
return value
return SON([("type", self._type), ("coordinates", value)])