documentoptions.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. import sys
  2. from collections import MutableMapping
  3. from types import MethodType
  4. from django.db.models.fields import FieldDoesNotExist
  5. from django.utils.text import capfirst
  6. try:
  7. from django.db.models.options import get_verbose_name
  8. except ImportError:
  9. from django.utils.text import camel_case_to_spaces as get_verbose_name
  10. from django.utils.functional import LazyObject
  11. from django.conf import settings
  12. from mongoengine.fields import ReferenceField, ListField
  13. def patch_document(function, instance, bound=True):
  14. if bound:
  15. method = MethodType(function, instance)
  16. else:
  17. method = function
  18. setattr(instance, function.__name__, method)
  19. def create_verbose_name(name):
  20. name = get_verbose_name(name)
  21. name = name.replace('_', ' ')
  22. return name
  23. class Relation(object):
  24. # just an empty dict to make it useable with Django
  25. # mongoengine has no notion of this
  26. limit_choices_to = {}
  27. def __init__(self, to):
  28. self._to = to
  29. @property
  30. def to(self):
  31. if not isinstance(self._to._meta, (DocumentMetaWrapper, LazyDocumentMetaWrapper)):
  32. self._to._meta = DocumentMetaWrapper(self._to)
  33. return self._to
  34. @to.setter
  35. def to(self, value):
  36. self._to = value
  37. class PkWrapper(object):
  38. editable = False
  39. fake = False
  40. def __init__(self, wrapped):
  41. self.obj = wrapped
  42. def __getattr__(self, attr):
  43. if attr in dir(self.obj):
  44. return getattr(self.obj, attr)
  45. raise AttributeError
  46. def __setattr__(self, attr, value):
  47. if attr != 'obj' and hasattr(self.obj, attr):
  48. setattr(self.obj, attr, value)
  49. super(PkWrapper, self).__setattr__(attr, value)
  50. class LazyDocumentMetaWrapper(LazyObject):
  51. _document = None
  52. _meta = None
  53. def __init__(self, document):
  54. self._document = document
  55. self._meta = document._meta
  56. super(LazyDocumentMetaWrapper, self).__init__()
  57. def _setup(self):
  58. self._wrapped = DocumentMetaWrapper(self._document, self._meta)
  59. def __setattr__(self, name, value):
  60. if name in ["_document", "_meta",]:
  61. object.__setattr__(self, name, value)
  62. else:
  63. super(LazyDocumentMetaWrapper, self).__setattr__(name, value)
  64. def __dir__(self):
  65. return self._wrapped.__dir__()
  66. def __getitem__(self, key):
  67. return self._wrapped.__getitem__(key)
  68. def __setitem__(self, key, value):
  69. return self._wrapped.__getitem__(key, value)
  70. def __delitem__(self, key):
  71. return self._wrapped.__delitem__(key)
  72. def __len__(self):
  73. return self._wrapped.__len__()
  74. def __contains__(self, key):
  75. return self._wrapped.__contains__(key)
  76. class DocumentMetaWrapper(MutableMapping):
  77. """
  78. Used to store mongoengine's _meta dict to make the document admin
  79. as compatible as possible to django's meta class on models.
  80. """
  81. # attributes Django deprecated. Not really sure when to remove them
  82. _deprecated_attrs = {'module_name': 'model_name'}
  83. pk = None
  84. pk_name = None
  85. _app_label = None
  86. model_name = None
  87. _verbose_name = None
  88. has_auto_field = False
  89. object_name = None
  90. proxy = []
  91. parents = {}
  92. many_to_many = []
  93. _field_cache = None
  94. document = None
  95. _meta = None
  96. concrete_model = None
  97. concrete_managers = []
  98. virtual_fields = []
  99. auto_created = False
  100. def __init__(self, document, meta=None):
  101. super(DocumentMetaWrapper, self).__init__()
  102. self.document = document
  103. # used by Django to distinguish between abstract and concrete models
  104. # here for now always the document
  105. self.concrete_model = document
  106. if meta is None:
  107. meta = getattr(document, '_meta', {})
  108. if isinstance(meta, LazyDocumentMetaWrapper):
  109. meta = meta._meta
  110. self._meta = meta
  111. try:
  112. self.object_name = self.document.__name__
  113. except AttributeError:
  114. self.object_name = self.document.__class__.__name__
  115. self.model_name = self.object_name.lower()
  116. # add the gluey stuff to the document and it's fields to make
  117. # everything play nice with Django
  118. self._setup_document_fields()
  119. # Setup self.pk if the document has an id_field in it's meta
  120. # if it doesn't have one it's an embedded document
  121. #if 'id_field' in self._meta:
  122. # self.pk_name = self._meta['id_field']
  123. self._init_pk()
  124. def _setup_document_fields(self):
  125. for f in self.document._fields.values():
  126. # Yay, more glue. Django expects fields to have a couple attributes
  127. # at least in the admin, probably in more places.
  128. if not hasattr(f, 'rel'):
  129. # need a bit more for actual reference fields here
  130. if isinstance(f, ReferenceField):
  131. f.rel = Relation(f.document_type)
  132. elif isinstance(f, ListField) and \
  133. isinstance(f.field, ReferenceField):
  134. f.field.rel = Relation(f.field.document_type)
  135. else:
  136. f.rel = None
  137. if not hasattr(f, 'verbose_name') or f.verbose_name is None:
  138. f.verbose_name = capfirst(create_verbose_name(f.name))
  139. if not hasattr(f, 'flatchoices'):
  140. flat = []
  141. if f.choices is not None:
  142. for choice, value in f.choices:
  143. if isinstance(value, (list, tuple)):
  144. flat.extend(value)
  145. else:
  146. flat.append((choice, value))
  147. f.flatchoices = flat
  148. if isinstance(f, ReferenceField) and not \
  149. isinstance(f.document_type._meta, (DocumentMetaWrapper, LazyDocumentMetaWrapper)) and \
  150. self.document != f.document_type:
  151. f.document_type._meta = LazyDocumentMetaWrapper(f.document_type)
  152. def _init_pk(self):
  153. """
  154. Adds a wrapper around the documents pk field. The wrapper object gets
  155. the attributes django expects on the pk field, like name and attname.
  156. The function also adds a _get_pk_val method to the document.
  157. """
  158. if 'id_field' in self._meta:
  159. self.pk_name = self._meta['id_field']
  160. pk_field = getattr(self.document, self.pk_name)
  161. else:
  162. pk_field = None
  163. self.pk = PkWrapper(pk_field)
  164. def _get_pk_val(self):
  165. return self._pk_val
  166. if pk_field is not None:
  167. self.pk.name = self.pk_name
  168. self.pk.attname = self.pk_name
  169. self.document._pk_val = pk_field
  170. patch_document(_get_pk_val, self.document)
  171. else:
  172. self.pk.fake = True
  173. # this is used in the admin and used to determine if the admin
  174. # needs to add a hidden pk field. It does not for embedded fields.
  175. # So we pretend to have an editable pk field and just ignore it otherwise
  176. self.pk.editable = True
  177. @property
  178. def app_label(self):
  179. if self._app_label is None:
  180. model_module = sys.modules[self.document.__module__]
  181. self._app_label = model_module.__name__.split('.')[-2]
  182. return self._app_label
  183. @property
  184. def verbose_name(self):
  185. """
  186. Returns the verbose name of the document.
  187. Checks the original meta dict first. If it is not found
  188. then generates a verbose name from the object name.
  189. """
  190. if self._verbose_name is None:
  191. verbose_name = self._meta.get('verbose_name', self.object_name)
  192. self._verbose_name = capfirst(create_verbose_name(verbose_name))
  193. return self._verbose_name
  194. @property
  195. def verbose_name_raw(self):
  196. return self.verbose_name
  197. @property
  198. def verbose_name_plural(self):
  199. return "%ss" % self.verbose_name
  200. def get_add_permission(self):
  201. return 'add_%s' % self.object_name.lower()
  202. def get_change_permission(self):
  203. return 'change_%s' % self.object_name.lower()
  204. def get_delete_permission(self):
  205. return 'delete_%s' % self.object_name.lower()
  206. def get_ordered_objects(self):
  207. return []
  208. def get_field_by_name(self, name):
  209. """
  210. Returns the (field_object, model, direct, m2m), where field_object is
  211. the Field instance for the given name, model is the model containing
  212. this field (None for local fields), direct is True if the field exists
  213. on this model, and m2m is True for many-to-many relations. When
  214. 'direct' is False, 'field_object' is the corresponding RelatedObject
  215. for this field (since the field doesn't have an instance associated
  216. with it).
  217. """
  218. if name in self.document._fields:
  219. field = self.document._fields[name]
  220. if isinstance(field, ReferenceField):
  221. return (field, field.document_type, False, False)
  222. else:
  223. return (field, None, True, False)
  224. else:
  225. raise FieldDoesNotExist('%s has no field named %r' %
  226. (self.object_name, name))
  227. def get_field(self, name, many_to_many=True):
  228. """
  229. Returns the requested field by name. Raises FieldDoesNotExist on error.
  230. """
  231. return self.get_field_by_name(name)[0]
  232. @property
  233. def swapped(self):
  234. """
  235. Has this model been swapped out for another? If so, return the model
  236. name of the replacement; otherwise, return None.
  237. For historical reasons, model name lookups using get_model() are
  238. case insensitive, so we make sure we are case insensitive here.
  239. NOTE: Not sure this is actually usefull for documents. So at the
  240. moment it's really only here because the admin wants it. It might
  241. prove usefull for someone though, so it's more then just a dummy.
  242. """
  243. if self._meta.get('swappable', False):
  244. model_label = '%s.%s' % (self.app_label, self.object_name.lower())
  245. swapped_for = getattr(settings, self.swappable, None)
  246. if swapped_for:
  247. try:
  248. swapped_label, swapped_object = swapped_for.split('.')
  249. except ValueError:
  250. # setting not in the format app_label.model_name
  251. # raising ImproperlyConfigured here causes problems with
  252. # test cleanup code - instead it is raised in
  253. # get_user_model or as part of validation.
  254. return swapped_for
  255. if '%s.%s' % (swapped_label, swapped_object.lower()) \
  256. not in (None, model_label):
  257. return swapped_for
  258. return None
  259. def __getattr__(self, name):
  260. if name in self._deprecated_attrs:
  261. return getattr(self, self._deprecated_attrs.get(name))
  262. try:
  263. return self._meta[name]
  264. except KeyError:
  265. raise AttributeError
  266. def __setattr__(self, name, value):
  267. if not hasattr(self, name):
  268. self._meta[name] = value
  269. else:
  270. super(DocumentMetaWrapper, self).__setattr__(name, value)
  271. def __contains__(self, key):
  272. return key in self._meta
  273. def __getitem__(self, key):
  274. return self._meta[key]
  275. def __setitem__(self, key, value):
  276. self._meta[key] = value
  277. def __delitem__(self, key):
  278. return self._meta.__delitem__(key)
  279. def __iter__(self):
  280. return self._meta.__iter__()
  281. def __len__(self):
  282. return self._meta.__len__()
  283. def get(self, key, default=None):
  284. try:
  285. return self.__getitem__(key)
  286. except KeyError:
  287. return default
  288. def get_parent_list(self):
  289. return []
  290. def get_all_related_objects(self, *args, **kwargs):
  291. return []
  292. def iteritems(self):
  293. return iter(self._meta.items())