Skip to content

Commit

Permalink
Merge pull request #1128 from iici-gli/master
Browse files Browse the repository at this point in the history
Fixed: ListField minus index assignment does not work #1119
  • Loading branch information
lafrech authored Sep 7, 2016
2 parents 3dc8105 + 2c4536e commit 05e40e5
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 55 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ that much better:
* Vicki Donchenko (https://github.com/kivistein)
* Emile Caron (https://github.com/emilecaron)
* Amit Lichtenberg (https://github.com/amitlicht)
* Gang Li (https://github.com/iici-gli)
* Lars Butler (https://github.com/larsbutler)
* George Macon (https://github.com/gmacon)
* Ashley Whetter (https://github.com/AWhetter)
Expand Down
2 changes: 2 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ Changes in 0.10.1
- Document save's save_condition error raises `SaveConditionError` exception #1070
- Fix Document.reload for DynamicDocument. #1050
- StrictDict & SemiStrictDict are shadowed at init time. #1105
- Fix ListField minus index assignment does not work. #1119
- Remove code that marks field as changed when the field has default but not existed in database #1126
- Remove test dependencies (nose and rednose) from install dependencies list. #1079
- Recursively build query when using elemMatch operator. #1130
- Fix instance back references for lists of embedded documents. #1131
Expand Down
34 changes: 16 additions & 18 deletions mongoengine/base/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ def to_mongo(self, use_db_field=True, fields=None):
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])

Expand All @@ -325,18 +325,20 @@ def to_mongo(self, use_db_field=True, fields=None):
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:
Expand Down Expand Up @@ -489,7 +491,7 @@ def _mark_as_changed(self, key):
# 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)

Expand Down Expand Up @@ -604,7 +606,9 @@ def _delta(self):
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:
Expand Down Expand Up @@ -638,7 +642,9 @@ def _delta(self):
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)):
Expand Down Expand Up @@ -706,14 +712,6 @@ def _from_son(cls, son, _auto_dereference=True, only_fields=None, created=False)
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)
Expand Down
35 changes: 23 additions & 12 deletions mongoengine/base/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,24 @@ def to_python(self, value):
"""
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.
"""
Expand Down Expand Up @@ -324,7 +337,7 @@ def to_python(self, value):
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")
Expand All @@ -336,10 +349,9 @@ def to_mongo(self, value, **kwargs):

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__
Expand All @@ -354,7 +366,7 @@ def to_mongo(self, value, **kwargs):
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 = {}
Expand All @@ -373,20 +385,19 @@ def to_mongo(self, value, **kwargs):
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(),
Expand Down Expand Up @@ -444,7 +455,7 @@ def to_python(self, value):
pass
return value

def to_mongo(self, value, **kwargs):
def to_mongo(self, value):
if not isinstance(value, ObjectId):
try:
return ObjectId(unicode(value))
Expand Down Expand Up @@ -619,7 +630,7 @@ def _validate_multipolygon(self, value):
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)])
52 changes: 28 additions & 24 deletions mongoengine/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ def to_python(self, value):
pass
return value

def to_mongo(self, value, **kwargs):
def to_mongo(self, value):
return Int64(value)

def validate(self, value):
Expand Down Expand Up @@ -338,7 +338,7 @@ def to_python(self, value):
return value
return value.quantize(decimal.Decimal(".%s" % ("0" * self.precision)), rounding=self.rounding)

def to_mongo(self, value, **kwargs):
def to_mongo(self, value):
if value is None:
return value
if self.force_string:
Expand Down Expand Up @@ -401,7 +401,7 @@ def validate(self, value):
if not isinstance(new_value, (datetime.datetime, datetime.date)):
self.error(u'cannot parse date "%s"' % value)

def to_mongo(self, value, **kwargs):
def to_mongo(self, value):
if value is None:
return value
if isinstance(value, datetime.datetime):
Expand Down Expand Up @@ -524,7 +524,7 @@ def to_python(self, value):
except Exception:
return original_value

def to_mongo(self, value, **kwargs):
def to_mongo(self, value):
value = self.to_python(value)
return self._convert_from_datetime(value)

Expand Down Expand Up @@ -559,10 +559,10 @@ def to_python(self, value):
return self.document_type._from_son(value, _auto_dereference=self._auto_dereference)
return value

def to_mongo(self, value, **kwargs):
def to_mongo(self, value, use_db_field=True, fields=None):
if not isinstance(value, self.document_type):
return value
return self.document_type.to_mongo(value, **kwargs)
return self.document_type.to_mongo(value, use_db_field, fields)

def validate(self, value, clean=True):
"""Make sure that the document instance is an instance of the
Expand Down Expand Up @@ -612,11 +612,11 @@ def validate(self, value, clean=True):

value.validate(clean=clean)

def to_mongo(self, document, **kwargs):
def to_mongo(self, document, use_db_field=True, fields=None):
if document is None:
return None

data = document.to_mongo(**kwargs)
data = document.to_mongo(use_db_field, fields)
if '_cls' not in data:
data['_cls'] = document._class_name
return data
Expand All @@ -628,7 +628,7 @@ class DynamicField(BaseField):
Used by :class:`~mongoengine.DynamicDocument` to handle dynamic data"""

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.
"""

Expand All @@ -637,7 +637,7 @@ def to_mongo(self, value, **kwargs):

if hasattr(value, 'to_mongo'):
cls = value.__class__
val = value.to_mongo(**kwargs)
val = value.to_mongo(use_db_field, fields)
# If we its a document thats not inherited add _cls
if isinstance(value, Document):
val = {"_ref": value.to_dbref(), "_cls": cls.__name__}
Expand All @@ -655,7 +655,7 @@ def to_mongo(self, value, **kwargs):

data = {}
for k, v in value.iteritems():
data[k] = self.to_mongo(v, **kwargs)
data[k] = self.to_mongo(v, use_db_field, fields)

value = data
if is_list: # Convert back to a list
Expand Down Expand Up @@ -767,8 +767,8 @@ def __init__(self, field, **kwargs):
self._order_reverse = kwargs.pop('reverse')
super(SortedListField, self).__init__(field, **kwargs)

def to_mongo(self, value, **kwargs):
value = super(SortedListField, self).to_mongo(value, **kwargs)
def to_mongo(self, value, use_db_field=True, fields=None):
value = super(SortedListField, self).to_mongo(value, use_db_field, fields)
if self._ordering is not None:
return sorted(value, key=itemgetter(self._ordering),
reverse=self._order_reverse)
Expand Down Expand Up @@ -954,7 +954,7 @@ def __get__(self, instance, owner):

return super(ReferenceField, self).__get__(instance, owner)

def to_mongo(self, document, **kwargs):
def to_mongo(self, document):
if isinstance(document, DBRef):
if not self.dbref:
return document.id
Expand All @@ -977,7 +977,7 @@ def to_mongo(self, document, **kwargs):
id_field_name = cls._meta['id_field']
id_field = cls._fields[id_field_name]

id_ = id_field.to_mongo(id_, **kwargs)
id_ = id_field.to_mongo(id_)
if self.document_type._meta.get('abstract'):
collection = cls._get_collection_name()
return DBRef(collection, id_, cls=cls._class_name)
Expand Down Expand Up @@ -1100,7 +1100,7 @@ def __get__(self, instance, owner):

return super(CachedReferenceField, self).__get__(instance, owner)

def to_mongo(self, document, **kwargs):
def to_mongo(self, document, use_db_field=True, fields=None):
id_field_name = self.document_type._meta['id_field']
id_field = self.document_type._fields[id_field_name]

Expand All @@ -1115,11 +1115,15 @@ def to_mongo(self, document, **kwargs):
# TODO: should raise here or will fail next statement

value = SON((
("_id", id_field.to_mongo(id_, **kwargs)),
("_id", id_field.to_mongo(id_)),
))

kwargs['fields'] = self.fields
value.update(dict(document.to_mongo(**kwargs)))
if fields:
new_fields = [f for f in self.fields if f in fields]
else:
new_fields = self.fields

value.update(dict(document.to_mongo(use_db_field, fields=new_fields)))
return value

def prepare_query_value(self, op, value):
Expand Down Expand Up @@ -1235,7 +1239,7 @@ def dereference(self, value):
doc = doc_cls._from_son(doc)
return doc

def to_mongo(self, document, **kwargs):
def to_mongo(self, document):
if document is None:
return None

Expand All @@ -1254,7 +1258,7 @@ def to_mongo(self, document, **kwargs):
else:
id_ = document

id_ = id_field.to_mongo(id_, **kwargs)
id_ = id_field.to_mongo(id_)
collection = document._get_collection_name()
ref = DBRef(collection, id_)
return SON((
Expand Down Expand Up @@ -1283,7 +1287,7 @@ def __set__(self, instance, value):
value = bin_type(value)
return super(BinaryField, self).__set__(instance, value)

def to_mongo(self, value, **kwargs):
def to_mongo(self, value):
return Binary(value)

def validate(self, value):
Expand Down Expand Up @@ -1508,7 +1512,7 @@ def get_proxy_obj(self, key, instance, db_alias=None, collection_name=None):
db_alias=db_alias,
collection_name=collection_name)

def to_mongo(self, value, **kwargs):
def to_mongo(self, value):
# Store the GridFS file id in MongoDB
if isinstance(value, self.proxy_class) and value.grid_id is not None:
return value.grid_id
Expand Down Expand Up @@ -1858,7 +1862,7 @@ def to_python(self, value):
return original_value
return value

def to_mongo(self, value, **kwargs):
def to_mongo(self, value):
if not self._binary:
return unicode(value)
elif isinstance(value, basestring):
Expand Down
Loading

0 comments on commit 05e40e5

Please sign in to comment.