ajax.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. import mongoengine
  2. from flask_admin._compat import string_types, as_unicode, iteritems
  3. from flask_admin.model.ajax import AjaxModelLoader, DEFAULT_PAGE_SIZE
  4. class QueryAjaxModelLoader(AjaxModelLoader):
  5. def __init__(self, name, model, **options):
  6. """
  7. Constructor.
  8. :param fields:
  9. Fields to run query against
  10. """
  11. super(QueryAjaxModelLoader, self).__init__(name, options)
  12. self.model = model
  13. self.fields = options.get('fields')
  14. self._cached_fields = self._process_fields()
  15. if not self.fields:
  16. raise ValueError('AJAX loading requires `fields` to be specified for %s.%s' % (model, self.name))
  17. def _process_fields(self):
  18. remote_fields = []
  19. for field in self.fields:
  20. if isinstance(field, string_types):
  21. attr = getattr(self.model, field, None)
  22. if not attr:
  23. raise ValueError('%s.%s does not exist.' % (self.model, field))
  24. remote_fields.append(attr)
  25. else:
  26. remote_fields.append(field)
  27. return remote_fields
  28. def format(self, model):
  29. if not model:
  30. return None
  31. return (as_unicode(model.id), as_unicode(model))
  32. def get_one(self, pk):
  33. return self.model.objects.filter(id=pk).first()
  34. def get_list(self, term, offset=0, limit=DEFAULT_PAGE_SIZE):
  35. query = self.model.objects
  36. criteria = None
  37. for field in self._cached_fields:
  38. flt = {u'%s__icontains' % field.name: term}
  39. if not criteria:
  40. criteria = mongoengine.Q(**flt)
  41. else:
  42. criteria |= mongoengine.Q(**flt)
  43. query = query.filter(criteria)
  44. if offset:
  45. query = query.skip(offset)
  46. return query.limit(limit).all()
  47. def create_ajax_loader(model, name, field_name, opts):
  48. prop = getattr(model, field_name, None)
  49. if prop is None:
  50. raise ValueError('Model %s does not have field %s.' % (model, field_name))
  51. ftype = type(prop).__name__
  52. if ftype == 'ListField' or ftype == 'SortedListField':
  53. prop = prop.field
  54. ftype = type(prop).__name__
  55. if ftype != 'ReferenceField':
  56. raise ValueError('Dont know how to convert %s type for AJAX loader' % ftype)
  57. remote_model = prop.document_type
  58. return QueryAjaxModelLoader(name, remote_model, **opts)
  59. def process_ajax_references(references, view):
  60. def make_name(base, name):
  61. if base:
  62. return ('%s-%s' % (base, name)).lower()
  63. else:
  64. return as_unicode(name).lower()
  65. def handle_field(field, subdoc, base):
  66. ftype = type(field).__name__
  67. if ftype == 'ListField' or ftype == 'SortedListField':
  68. child_doc = getattr(subdoc, '_form_subdocuments', {}).get(None)
  69. if child_doc:
  70. handle_field(field.field, child_doc, base)
  71. elif ftype == 'EmbeddedDocumentField':
  72. result = {}
  73. ajax_refs = getattr(subdoc, 'form_ajax_refs', {})
  74. for field_name, opts in iteritems(ajax_refs):
  75. child_name = make_name(base, field_name)
  76. if isinstance(opts, dict):
  77. loader = create_ajax_loader(field.document_type_obj, child_name, field_name, opts)
  78. else:
  79. loader = opts
  80. result[field_name] = loader
  81. references[child_name] = loader
  82. subdoc._form_ajax_refs = result
  83. child_doc = getattr(subdoc, '_form_subdocuments', None)
  84. if child_doc:
  85. handle_subdoc(field.document_type_obj, subdoc, base)
  86. else:
  87. raise ValueError('Failed to process subdocument field %s' % (field,))
  88. def handle_subdoc(model, subdoc, base):
  89. documents = getattr(subdoc, '_form_subdocuments', {})
  90. for name, doc in iteritems(documents):
  91. field = getattr(model, name, None)
  92. if not field:
  93. raise ValueError('Invalid subdocument field %s.%s' % (model, name))
  94. handle_field(field, doc, make_name(base, name))
  95. handle_subdoc(view.model, view, '')
  96. return references