text: import logging
  
  import pymongo
  from bson import ObjectId
  from bson.errors import InvalidId
  
  from flask import flash
  
  from flask_admin._compat import string_types
  from flask_admin.babel import gettext, ngettext, lazy_gettext
  from flask_admin.model import BaseModelView
  from flask_admin.actions import action
  from flask_admin.helpers import get_form_data
  
  from .filters import BasePyMongoFilter
  from .tools import parse_like_term
  from .form import get_form, CustomModelConverter
  # Set up logger
  log = logging.getLogger("flask-admin.humbledb")
  
  
  class ModelView(BaseModelView):
      """
          MongoEngine model scaffolding.
      """
  
      column_filters = None
      """
          Collection of the column filters.
  
          Should contain instances of
          :class:`flask_admin.contrib.pymongo.filters.BasePyMongoFilter` classes.
  
          Filters will be grouped by name when displayed in the drop-down.
  
          For example::
  
              from flask_admin.contrib.pymongo.filters import BooleanEqualFilter
  
              class MyModelView(BaseModelView):
                  column_filters = (BooleanEqualFilter(column=User.name, name='Name'),)
  
          or::
  
              from flask_admin.contrib.pymongo.filters import BasePyMongoFilter
  
              class FilterLastNameBrown(BasePyMongoFilter):
                  def apply(self, query, value):
                      if value == '1':
                          return query.filter(self.column == "Brown")
                      else:
                          return query.filter(self.column != "Brown")
  
                  def operation(self):
                      return 'is Brown'
  
              class MyModelView(BaseModelView):
                  column_filters = [
                      FilterLastNameBrown(
                          column=User.last_name, name='Last Name',
                          options=(('1', 'Yes'), ('0', 'No'))
                      )
                  ]
      """
      model_form_converter = CustomModelConverter
  
      def __init__(self, con_ctx, coll,
                   name=None, category=None, endpoint=None, url=None,
                   menu_class_name=None, menu_icon_type=None, menu_icon_value=None):
          """
              Constructor
  
              :param coll:
                  MongoDB collection object
              :param name:
                  Display name
              :param category:
                  Display category
              :param endpoint:
                  Endpoint
              :param url:
                  Custom URL
              :param menu_class_name:
                  Optional class name for the menu item.
              :param menu_icon_type:
                  Optional icon. Possible icon types:
  
                   - `flask_admin.consts.ICON_TYPE_GLYPH` - Bootstrap glyph icon
                   - `flask_admin.consts.ICON_TYPE_FONT_AWESOME` - Font Awesome icon
                   - `flask_admin.consts.ICON_TYPE_IMAGE` - Image relative to Flask static directory
                   - `flask_admin.consts.ICON_TYPE_IMAGE_URL` - Image with full URL
              :param menu_icon_value:
                  Icon glyph name or URL, depending on `menu_icon_type` setting
          """
          self._search_fields = []
  
          if name is None:
              name = self._prettify_name(coll.name)
  
          if endpoint is None:
              endpoint = ('%sview' % coll.config_collection).lower()
  
          self.con_ctx = con_ctx
          self.coll = coll
  
          super(ModelView, self).__init__(None, name, category, endpoint, url,
                                          menu_class_name=menu_class_name,
                                          menu_icon_type=menu_icon_type,
                                          menu_icon_value=menu_icon_value)
  
      def scaffold_pk(self):
          return '_id'
  
      def get_pk_value(self, model):
          """
              Return primary key value from the model instance
  
              :param model:
                  Model instance
          """
          return model.get('_id')
  
      def scaffold_list_columns(self):
          """
              Scaffold list columns
          """
          raise NotImplementedError()
  
      def scaffold_sortable_columns(self):
          """
              Return sortable columns dictionary (name, field)
          """
          return []
  
      def init_search(self):
          """
              Init search
          """
          if self.column_searchable_list:
              for p in self.column_searchable_list:
                  if not isinstance(p, string_types):
                      raise ValueError('Expected string')
  
                  # TODO: Validation?
  
                  self._search_fields.append(p)
  
          return bool(self._search_fields)
  
      def scaffold_filters(self, attr):
          """
              Return filter object(s) for the field
  
              :param name:
                  Either field name or field instance
          """
          raise NotImplementedError()
  
      def is_valid_filter(self, filter):
          """
              Validate if it is valid MongoEngine filter
  
              :param filter:
                  Filter object
          """
          return isinstance(filter, BasePyMongoFilter)
  
      def scaffold_form(self):
          # raise NotImplementedError()
          # return self.form_base_class
          form_class = get_form(self.coll,
                                self.model_form_converter(self),
                                base_class=self.form_base_class,
                                only=self.form_columns,
                                exclude=self.form_excluded_columns,
                                field_args=self.form_args,
                                extra_fields=self.form_extra_fields)
  
          return form_class
  
      def _get_field_value(self, model, name):
          """
              Get unformatted field value from the model
          """
          return model.get(name)
  
      def _search(self, query, search_term):
          values = search_term.split(' ')
  
          queries = []
  
          # Construct inner querie
          for value in values:
              if not value:
                  continue
  
              regex = parse_like_term(value)
  
              stmt = []
              for field in self._search_fields:
                  # field = getattr(self.coll, field, field)
                  stmt.append({field: {'$regex': regex}})
  
              if stmt:
                  if len(stmt) == 1:
                      queries.append(stmt[0])
                  else:
                      queries.append({'$or': stmt})
  
          # Construct final query
          if queries:
              if len(queries) == 1:
                  final = queries[0]
              else:
                  final = {'$and': queries}
  
              if query:
                  query = {'$and': [query, final]}
              else:
                  query = final
  
          return query
  
      def _get_list_value(self, context, model, name, column_formatters,
                          column_type_formatters):
          '''map field name to short name.
          '''
          # name = getattr(self.coll, name, name)
          return super()._get_list_value(
              context, model, name, column_formatters, column_type_formatters,
          )
  
      def get_list(self, page, sort_column, sort_desc, search, filters,
                   execute=True, page_size=None):
          """
              Get list of objects from MongoEngine
  
              :param page:
                  Page number
              :param sort_column:
                  Sort column
              :param sort_desc:
                  Sort descending
              :param search:
                  Search criteria
              :param filters:
                  List of applied fiters
              :param execute:
                  Run query immediately or not
              :param page_size:
                  Number of results. Defaults to ModelView's page_size. Can be
                  overriden to change the page_size limit. Removing the page_size
                  limit requires setting page_size to 0 or False.
          """
          query = {}
  
          # Filters
          if self._filters:
              data = []
  
              for flt, flt_name, value in filters:
                  # print(self._filters)
                  f = self._filters[flt]
                  # print(f, type(f))
                  data = f.apply(data, f.clean(value))
  
              if data:
                  # print(data)
                  if len(data) == 1:
                      query = data[0]
                  else:
                      query['$and'] = data
  
          # Search
          if self._search_supported and search:
              query = self._search(query, search)
  
          # Get count
          with self.con_ctx:
              count = self.coll.find(query).count() if not self.simple_list_pager else None
  
          # Sorting
          sort_by = None
  
          if sort_column:
              # sort_column = getattr(self.coll, sort_column, sort_column)
              sort_by = [(sort_column, pymongo.DESCENDING if sort_desc else pymongo.ASCENDING)]
          else:
              order = self._get_default_order()
  
              if order:
                  sort_by = [(col, pymongo.DESCENDING if desc else pymongo.ASCENDING)
                             for (col, desc) in order]
  
          # Pagination
          if page_size is None:
              page_size = self.page_size
  
          skip = 0
  
          if page and page_size:
              skip = page * page_size
  
          with self.con_ctx:
              results = self.coll.find(query, sort=sort_by, skip=skip, limit=page_size)
  
          if execute:
              results = list(results)
          return count, results
  
      def _get_valid_id(self, id):
          try:
              return ObjectId(id)
          except InvalidId:
              return id
  
      def get_one(self, id):
          """
              Return single model instance by ID
  
              :param id:
                  Model ID
          """
          with self.con_ctx:
              return self.coll.find_one({'_id': self._get_valid_id(id)})
  
      def edit_form(self, obj):
          """
              Create edit form from the MongoDB document
          """
          return self._edit_form_class(get_form_data(), **obj)
  
      def create_model(self, form):
          """
              Create model helper
  
              :param form:
                  Form instance
          """
          try:
              model = form.data
              self._on_model_change(form, model, True)
              with self.con_ctx:
                  self.coll.insert(model)
          except Exception as ex:
              flash(gettext('Failed to create record. %(error)s', error=str(ex)),
                    'error')
              log.exception('Failed to create record.')
              return False
          else:
              self.after_model_change(form, model, True)
  
          return model
  
      def update_model(self, form, model):
          """
              Update model helper
  
              :param form:
                  Form instance
              :param model:
                  Model instance to update
          """
          try:
              model.update(form.data)
              self._on_model_change(form, model, False)
              pk = self.get_pk_value(model)
              with self.con_ctx:
                  self.coll.update({'_id': pk}, model)
          except Exception as ex:
              flash(gettext('Failed to update record. %(error)s', error=str(ex)),
                    'error')
              log.exception('Failed to update record.')
              return False
          else:
              self.after_model_change(form, model, False)
  
          return True
  
      def delete_model(self, model):
          """
              Delete model helper
  
              :param model:
                  Model instance
          """
          try:
              pk = self.get_pk_value(model)
  
              if not pk:
                  raise ValueError('Document does not have _id')
  
              self.on_model_delete(model)
              with self.con_ctx:
                  self.coll.remove({'_id': pk})
          except Exception as ex:
              flash(gettext('Failed to delete record. %(error)s', error=str(ex)),
                    'error')
              log.exception('Failed to delete record.')
              return False
          else:
              self.after_model_delete(model)
  
          return True
  
      # Default model actions
      def is_action_allowed(self, name):
          # Check delete action permission
          if name == 'delete' and not self.can_delete:
              return False
  
          return super(ModelView, self).is_action_allowed(name)
  
      @action('delete',
              lazy_gettext('Delete'),
              lazy_gettext('Are you sure you want to delete selected records?'))
      def action_delete(self, ids):
          try:
              count = 0
  
              # TODO: Optimize me
              for pk in ids:
                  if self.delete_model(self.get_one(pk)):
                      count += 1
  
              flash(ngettext('Record was successfully deleted.',
                             '%(count)s records were successfully deleted.',
                             count,
                             count=count), 'success')
          except Exception as ex:
              flash(gettext('Failed to delete records. %(error)s', error=str(ex)), 'error')
  