document and slightly simplify BaseDocument._lookup_field

This commit is contained in:
Stefan Wojcik 2016-12-08 17:41:40 -05:00
parent 4b024409ba
commit 9a32ff4c42

View File

@ -913,15 +913,47 @@ class BaseDocument(object):
@classmethod @classmethod
def _lookup_field(cls, parts): def _lookup_field(cls, parts):
"""Lookup a field based on its attribute and return a list containing """Given the path to a given field, return a list containing
the field's parents and the field. the Field object associated with that field and all of its parent
Field objects.
Args:
parts (str, list, or tuple) - path to the field. Should be a
string for simple fields existing on this document or a list
of strings for a field that exists deeper in embedded documents.
Returns:
A list of Field instances for fields that were found or
strings for sub-fields that weren't.
Example:
>>> user._lookup_field('name')
[<mongoengine.fields.StringField at 0x1119bff50>]
>>> user._lookup_field('roles')
[<mongoengine.fields.EmbeddedDocumentListField at 0x1119ec250>]
>>> user._lookup_field(['roles', 'role'])
[<mongoengine.fields.EmbeddedDocumentListField at 0x1119ec250>,
<mongoengine.fields.StringField at 0x1119ec050>]
>>> user._lookup_field('doesnt_exist')
raises LookUpError
>>> user._lookup_field(['roles', 'doesnt_exist'])
[<mongoengine.fields.EmbeddedDocumentListField at 0x1119ec250>,
'doesnt_exist']
""" """
# TODO this method is WAY too complicated. Simplify it.
# TODO don't think returning a string for embedded non-existent fields is desired
ListField = _import_class("ListField") ListField = _import_class("ListField")
DynamicField = _import_class('DynamicField') DynamicField = _import_class('DynamicField')
if not isinstance(parts, (list, tuple)): if not isinstance(parts, (list, tuple)):
parts = [parts] parts = [parts]
fields = [] fields = []
field = None field = None
@ -931,16 +963,17 @@ class BaseDocument(object):
fields.append(field_name) fields.append(field_name)
continue continue
# Look up first field from the document
if field is None: if field is None:
# Look up first field from the document
if field_name == 'pk': if field_name == 'pk':
# Deal with "primary key" alias # Deal with "primary key" alias
field_name = cls._meta['id_field'] field_name = cls._meta['id_field']
if field_name in cls._fields: if field_name in cls._fields:
field = cls._fields[field_name] field = cls._fields[field_name]
elif cls._dynamic: elif cls._dynamic:
field = DynamicField(db_field=field_name) field = DynamicField(db_field=field_name)
elif cls._meta.get("allow_inheritance", False) or cls._meta.get("abstract", False): elif cls._meta.get('allow_inheritance') or cls._meta.get('abstract', False):
# 744: in case the field is defined in a subclass # 744: in case the field is defined in a subclass
for subcls in cls.__subclasses__(): for subcls in cls.__subclasses__():
try: try:
@ -953,35 +986,55 @@ class BaseDocument(object):
else: else:
raise LookUpError('Cannot resolve field "%s"' % field_name) raise LookUpError('Cannot resolve field "%s"' % field_name)
else: else:
raise LookUpError('Cannot resolve field "%s"' raise LookUpError('Cannot resolve field "%s"' % field_name)
% field_name)
else: else:
ReferenceField = _import_class('ReferenceField') ReferenceField = _import_class('ReferenceField')
GenericReferenceField = _import_class('GenericReferenceField') GenericReferenceField = _import_class('GenericReferenceField')
# If previous field was a reference, throw an error (we
# cannot look up fields that are on references).
if isinstance(field, (ReferenceField, GenericReferenceField)): if isinstance(field, (ReferenceField, GenericReferenceField)):
raise LookUpError('Cannot perform join in mongoDB: %s' % raise LookUpError('Cannot perform join in mongoDB: %s' %
'__'.join(parts)) '__'.join(parts))
# If the parent field has a "field" attribute which has a
# lookup_member method, call it to find the field
# corresponding to this iteration.
if hasattr(getattr(field, 'field', None), 'lookup_member'): if hasattr(getattr(field, 'field', None), 'lookup_member'):
new_field = field.field.lookup_member(field_name) new_field = field.field.lookup_member(field_name)
# If the parent field is a DynamicField or if it's part of
# a DynamicDocument, mark current field as a DynamicField
# with db_name equal to the field name.
elif cls._dynamic and (isinstance(field, DynamicField) or elif cls._dynamic and (isinstance(field, DynamicField) or
getattr(getattr(field, 'document_type', None), '_dynamic', None)): getattr(getattr(field, 'document_type', None), '_dynamic', None)):
new_field = DynamicField(db_field=field_name) new_field = DynamicField(db_field=field_name)
# Else, try to use the parent field's lookup_member method
# to find the subfield.
elif hasattr(field, 'lookup_member'):
new_field = field.lookup_member(field_name)
# Raise a LookUpError if all the other conditions failed.
else: else:
# Look up subfield on the previous field or raise raise LookUpError(
try: 'Cannot resolve subfield or operator {} '
new_field = field.lookup_member(field_name) 'on the field {}'.format(field_name, field.name)
except AttributeError: )
raise LookUpError('Cannot resolve subfield or operator {} '
'on the field {}'.format( # If current field still wasn't found and the parent field
field_name, field.name)) # is a ComplexBaseField, add the name current field name and
# move on.
if not new_field and isinstance(field, ComplexBaseField): if not new_field and isinstance(field, ComplexBaseField):
fields.append(field_name) fields.append(field_name)
continue continue
elif not new_field: elif not new_field:
raise LookUpError('Cannot resolve field "%s"' raise LookUpError('Cannot resolve field "%s"' % field_name)
% field_name)
field = new_field # update field to the new field type field = new_field # update field to the new field type
fields.append(field) fields.append(field)
return fields return fields
@classmethod @classmethod