dbref.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. # Copyright 2009-2015 MongoDB, Inc.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """Tools for manipulating DBRefs (references to MongoDB documents)."""
  15. from copy import deepcopy
  16. from bson.py3compat import iteritems, string_type
  17. from bson.son import SON
  18. class DBRef(object):
  19. """A reference to a document stored in MongoDB.
  20. """
  21. # DBRef isn't actually a BSON "type" so this number was arbitrarily chosen.
  22. _type_marker = 100
  23. def __init__(self, collection, id, database=None, _extra={}, **kwargs):
  24. """Initialize a new :class:`DBRef`.
  25. Raises :class:`TypeError` if `collection` or `database` is not
  26. an instance of :class:`basestring` (:class:`str` in python 3).
  27. `database` is optional and allows references to documents to work
  28. across databases. Any additional keyword arguments will create
  29. additional fields in the resultant embedded document.
  30. :Parameters:
  31. - `collection`: name of the collection the document is stored in
  32. - `id`: the value of the document's ``"_id"`` field
  33. - `database` (optional): name of the database to reference
  34. - `**kwargs` (optional): additional keyword arguments will
  35. create additional, custom fields
  36. .. mongodoc:: dbrefs
  37. """
  38. if not isinstance(collection, string_type):
  39. raise TypeError("collection must be an "
  40. "instance of %s" % string_type.__name__)
  41. if database is not None and not isinstance(database, string_type):
  42. raise TypeError("database must be an "
  43. "instance of %s" % string_type.__name__)
  44. self.__collection = collection
  45. self.__id = id
  46. self.__database = database
  47. kwargs.update(_extra)
  48. self.__kwargs = kwargs
  49. @property
  50. def collection(self):
  51. """Get the name of this DBRef's collection as unicode.
  52. """
  53. return self.__collection
  54. @property
  55. def id(self):
  56. """Get this DBRef's _id.
  57. """
  58. return self.__id
  59. @property
  60. def database(self):
  61. """Get the name of this DBRef's database.
  62. Returns None if this DBRef doesn't specify a database.
  63. """
  64. return self.__database
  65. def __getattr__(self, key):
  66. try:
  67. return self.__kwargs[key]
  68. except KeyError:
  69. raise AttributeError(key)
  70. # Have to provide __setstate__ to avoid
  71. # infinite recursion since we override
  72. # __getattr__.
  73. def __setstate__(self, state):
  74. self.__dict__.update(state)
  75. def as_doc(self):
  76. """Get the SON document representation of this DBRef.
  77. Generally not needed by application developers
  78. """
  79. doc = SON([("$ref", self.collection),
  80. ("$id", self.id)])
  81. if self.database is not None:
  82. doc["$db"] = self.database
  83. doc.update(self.__kwargs)
  84. return doc
  85. def __repr__(self):
  86. extra = "".join([", %s=%r" % (k, v)
  87. for k, v in iteritems(self.__kwargs)])
  88. if self.database is None:
  89. return "DBRef(%r, %r%s)" % (self.collection, self.id, extra)
  90. return "DBRef(%r, %r, %r%s)" % (self.collection, self.id,
  91. self.database, extra)
  92. def __eq__(self, other):
  93. if isinstance(other, DBRef):
  94. us = (self.__database, self.__collection,
  95. self.__id, self.__kwargs)
  96. them = (other.__database, other.__collection,
  97. other.__id, other.__kwargs)
  98. return us == them
  99. return NotImplemented
  100. def __ne__(self, other):
  101. return not self == other
  102. def __hash__(self):
  103. """Get a hash value for this :class:`DBRef`."""
  104. return hash((self.__collection, self.__id, self.__database,
  105. tuple(sorted(self.__kwargs.items()))))
  106. def __deepcopy__(self, memo):
  107. """Support function for `copy.deepcopy()`."""
  108. return DBRef(deepcopy(self.__collection, memo),
  109. deepcopy(self.__id, memo),
  110. deepcopy(self.__database, memo),
  111. deepcopy(self.__kwargs, memo))