interface.py 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. import logging
  2. import sys
  3. from mongoengine.fields import (
  4. BooleanField,
  5. DateTimeField,
  6. FileField,
  7. FloatField,
  8. ImageField,
  9. IntField,
  10. ListField,
  11. ObjectIdField,
  12. ReferenceField,
  13. StringField,
  14. )
  15. from . import filters
  16. from ..base import BaseInterface
  17. from ..._compat import as_unicode
  18. from ...const import (
  19. LOGMSG_ERR_DBI_ADD_GENERIC,
  20. LOGMSG_ERR_DBI_DEL_GENERIC,
  21. LOGMSG_ERR_DBI_EDIT_GENERIC,
  22. )
  23. log = logging.getLogger(__name__)
  24. def _include_filters(obj):
  25. for key in filters.__all__:
  26. if not hasattr(obj, key):
  27. setattr(obj, key, getattr(filters, key))
  28. class MongoEngineInterface(BaseInterface):
  29. filter_converter_class = filters.MongoEngineFilterConverter
  30. def __init__(self, obj, session=None):
  31. self.session = session
  32. _include_filters(self)
  33. super(MongoEngineInterface, self).__init__(obj)
  34. @property
  35. def model_name(self):
  36. """
  37. Returns the models class name
  38. useful for auto title on views
  39. """
  40. return self.obj.__name__
  41. def query(
  42. self,
  43. filters=None,
  44. order_column="",
  45. order_direction="",
  46. page=None,
  47. page_size=None,
  48. ):
  49. # base query : all objects
  50. objs = self.obj.objects
  51. # apply filters first if given
  52. if filters:
  53. objs = filters.apply_all(objs)
  54. # get the count of all items, either filtered or unfiltered
  55. count = objs.count()
  56. # order the data
  57. if order_column != "":
  58. if hasattr(getattr(self.obj, order_column), "_col_name"):
  59. order_column = getattr(getattr(self.obj, order_column), "_col_name")
  60. if order_direction == "asc":
  61. objs = objs.order_by("-{0}".format(order_column))
  62. else:
  63. objs = objs.order_by("+{0}".format(order_column))
  64. if page_size is None: # error checking and warnings
  65. if page is not None:
  66. log.error("Attempting to get page %s but page_size is undefined", page)
  67. if count > 100:
  68. log.warn("Retrieving %s %s items from DB", count, self.obj)
  69. else: # get data segment for paginated page
  70. offset = (page or 0) * page_size
  71. objs = objs[offset : offset + page_size]
  72. return count, objs
  73. def is_object_id(self, col_name):
  74. try:
  75. return isinstance(self.obj._fields[col_name], ObjectIdField)
  76. except Exception:
  77. return False
  78. def is_string(self, col_name):
  79. try:
  80. return isinstance(self.obj._fields[col_name], StringField)
  81. except Exception:
  82. return False
  83. def is_integer(self, col_name):
  84. try:
  85. return isinstance(self.obj._fields[col_name], IntField)
  86. except Exception:
  87. return False
  88. def is_float(self, col_name):
  89. try:
  90. return isinstance(self.obj._fields[col_name], FloatField)
  91. except Exception:
  92. return False
  93. def is_boolean(self, col_name):
  94. try:
  95. return isinstance(self.obj._fields[col_name], BooleanField)
  96. except Exception:
  97. return False
  98. def is_datetime(self, col_name):
  99. try:
  100. return isinstance(self.obj._fields[col_name], DateTimeField)
  101. except Exception:
  102. return False
  103. def is_gridfs_file(self, col_name):
  104. try:
  105. return isinstance(self.obj._fields[col_name], FileField)
  106. except Exception:
  107. return False
  108. def is_gridfs_image(self, col_name):
  109. try:
  110. return isinstance(self.obj._fields[col_name], ImageField)
  111. except Exception:
  112. return False
  113. def is_relation(self, col_name):
  114. try:
  115. return isinstance(self.obj._fields[col_name], ReferenceField) or isinstance(
  116. self.obj._fields[col_name], ListField
  117. )
  118. except Exception:
  119. return False
  120. def is_relation_many_to_one(self, col_name):
  121. try:
  122. return isinstance(self.obj._fields[col_name], ReferenceField)
  123. except Exception:
  124. return False
  125. def is_relation_many_to_many(self, col_name):
  126. try:
  127. field = self.obj._fields[col_name]
  128. return isinstance(field, ListField) and isinstance(
  129. field.field, ReferenceField
  130. )
  131. except Exception:
  132. return False
  133. def is_relation_one_to_one(self, col_name):
  134. return False
  135. def is_relation_one_to_many(self, col_name):
  136. return False
  137. def is_nullable(self, col_name):
  138. return not self.obj._fields[col_name].required
  139. def is_unique(self, col_name):
  140. return self.obj._fields[col_name].unique
  141. def is_pk(self, col_name):
  142. return col_name == "id"
  143. def is_pk_composite(self):
  144. return False
  145. def get_max_length(self, col_name):
  146. try:
  147. col = self.obj._fields[col_name]
  148. if col.max_length:
  149. return col.max_length
  150. else:
  151. return -1
  152. except Exception:
  153. return -1
  154. def get_min_length(self, col_name):
  155. try:
  156. col = self.obj._fields[col_name]
  157. if col.min_length:
  158. return col.min_length
  159. else:
  160. return -1
  161. except Exception:
  162. return -1
  163. def add(self, item):
  164. try:
  165. item.save()
  166. self.message = (as_unicode(self.add_row_message), "success")
  167. return True
  168. except Exception as e:
  169. self.message = (
  170. as_unicode(self.general_error_message + " " + str(sys.exc_info()[0])),
  171. "danger",
  172. )
  173. log.exception(LOGMSG_ERR_DBI_ADD_GENERIC, e)
  174. return False
  175. def edit(self, item):
  176. try:
  177. item.save()
  178. self.message = (as_unicode(self.edit_row_message), "success")
  179. return True
  180. except Exception as e:
  181. self.message = (
  182. as_unicode(self.general_error_message + " " + str(sys.exc_info()[0])),
  183. "danger",
  184. )
  185. log.exception(LOGMSG_ERR_DBI_EDIT_GENERIC, e)
  186. return False
  187. def delete(self, item):
  188. try:
  189. item.delete()
  190. self.message = (as_unicode(self.delete_row_message), "success")
  191. return True
  192. except Exception as e:
  193. self.message = (
  194. as_unicode(self.general_error_message + " " + str(sys.exc_info()[0])),
  195. "danger",
  196. )
  197. log.exception(LOGMSG_ERR_DBI_DEL_GENERIC, e)
  198. return False
  199. def get_columns_list(self):
  200. """
  201. modified: removing the '_cls' column added by Mongoengine to support
  202. mongodb document inheritance
  203. cf. http://docs.mongoengine.org/apireference.html#documents:
  204. "A Document subclass may be itself subclassed,
  205. to create a specialised version of the document that will be
  206. stored in the same collection.
  207. To facilitate this behaviour a _cls field is added to documents
  208. (hidden though the MongoEngine interface).
  209. To disable this behaviour and remove the dependence on the presence of _cls set
  210. allow_inheritance to False in the meta dictionary."
  211. """
  212. columns = list(self.obj._fields.keys())
  213. if "_cls" in columns:
  214. columns.remove("_cls")
  215. return columns
  216. def get_search_columns_list(self):
  217. ret_lst = list()
  218. for col_name in self.get_columns_list():
  219. for conversion in self.filter_converter_class.conversion_table:
  220. if getattr(self, conversion[0])(col_name) and not self.is_object_id(
  221. col_name
  222. ):
  223. ret_lst.append(col_name)
  224. return ret_lst
  225. def get_user_columns_list(self):
  226. """
  227. Returns all model's columns except pk
  228. """
  229. return [
  230. col_name for col_name in self.get_columns_list() if not self.is_pk(col_name)
  231. ]
  232. def get_order_columns_list(self, list_columns=None):
  233. """
  234. Returns the columns that can be ordered
  235. :param list_columns: optional list of columns name, if provided will
  236. use this list only.
  237. """
  238. ret_lst = list()
  239. list_columns = list_columns or self.get_columns_list()
  240. for col_name in list_columns:
  241. if hasattr(self.obj, col_name):
  242. if not hasattr(getattr(self.obj, col_name), "__call__"):
  243. ret_lst.append(col_name)
  244. else:
  245. ret_lst.append(col_name)
  246. return ret_lst
  247. def get_related_model(self, col_name):
  248. field = self.obj._fields[col_name]
  249. if isinstance(field, ListField):
  250. return field.field.document_type
  251. else:
  252. return field.document_type
  253. def get_related_interface(self, col_name):
  254. return self.__class__(self.get_related_model(col_name))
  255. def get_related_obj(self, col_name, value):
  256. rel_model = self.get_related_model(col_name)
  257. return rel_model.objects(pk=value)[0]
  258. def get_keys(self, lst):
  259. """
  260. return a list of pk values from object list
  261. """
  262. pk_name = self.get_pk_name()
  263. return [getattr(item, pk_name) for item in lst]
  264. def get_related_fk(self, model):
  265. for col_name in self.get_columns_list():
  266. if self.is_relation(col_name):
  267. if model == self.get_related_model(col_name):
  268. return col_name
  269. def get_pk_name(self):
  270. return "id"
  271. def get(self, id, filters=None):
  272. if filters:
  273. objs = self.obj.objects
  274. objs = filters.apply_all(objs)
  275. return objs(pk=id).first()
  276. return self.obj.objects(pk=id).first()