exc.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. # orm/exc.py
  2. # Copyright (C) 2005-2017 the SQLAlchemy authors and contributors
  3. # <see AUTHORS file>
  4. #
  5. # This module is part of SQLAlchemy and is released under
  6. # the MIT License: http://www.opensource.org/licenses/mit-license.php
  7. """SQLAlchemy ORM exceptions."""
  8. from .. import exc as sa_exc, util
  9. NO_STATE = (AttributeError, KeyError)
  10. """Exception types that may be raised by instrumentation implementations."""
  11. class StaleDataError(sa_exc.SQLAlchemyError):
  12. """An operation encountered database state that is unaccounted for.
  13. Conditions which cause this to happen include:
  14. * A flush may have attempted to update or delete rows
  15. and an unexpected number of rows were matched during
  16. the UPDATE or DELETE statement. Note that when
  17. version_id_col is used, rows in UPDATE or DELETE statements
  18. are also matched against the current known version
  19. identifier.
  20. * A mapped object with version_id_col was refreshed,
  21. and the version number coming back from the database does
  22. not match that of the object itself.
  23. * A object is detached from its parent object, however
  24. the object was previously attached to a different parent
  25. identity which was garbage collected, and a decision
  26. cannot be made if the new parent was really the most
  27. recent "parent".
  28. .. versionadded:: 0.7.4
  29. """
  30. ConcurrentModificationError = StaleDataError
  31. class FlushError(sa_exc.SQLAlchemyError):
  32. """A invalid condition was detected during flush()."""
  33. class UnmappedError(sa_exc.InvalidRequestError):
  34. """Base for exceptions that involve expected mappings not present."""
  35. class ObjectDereferencedError(sa_exc.SQLAlchemyError):
  36. """An operation cannot complete due to an object being garbage
  37. collected.
  38. """
  39. class DetachedInstanceError(sa_exc.SQLAlchemyError):
  40. """An attempt to access unloaded attributes on a
  41. mapped instance that is detached."""
  42. class UnmappedInstanceError(UnmappedError):
  43. """An mapping operation was requested for an unknown instance."""
  44. @util.dependencies("sqlalchemy.orm.base")
  45. def __init__(self, base, obj, msg=None):
  46. if not msg:
  47. try:
  48. base.class_mapper(type(obj))
  49. name = _safe_cls_name(type(obj))
  50. msg = ("Class %r is mapped, but this instance lacks "
  51. "instrumentation. This occurs when the instance "
  52. "is created before sqlalchemy.orm.mapper(%s) "
  53. "was called." % (name, name))
  54. except UnmappedClassError:
  55. msg = _default_unmapped(type(obj))
  56. if isinstance(obj, type):
  57. msg += (
  58. '; was a class (%s) supplied where an instance was '
  59. 'required?' % _safe_cls_name(obj))
  60. UnmappedError.__init__(self, msg)
  61. def __reduce__(self):
  62. return self.__class__, (None, self.args[0])
  63. class UnmappedClassError(UnmappedError):
  64. """An mapping operation was requested for an unknown class."""
  65. def __init__(self, cls, msg=None):
  66. if not msg:
  67. msg = _default_unmapped(cls)
  68. UnmappedError.__init__(self, msg)
  69. def __reduce__(self):
  70. return self.__class__, (None, self.args[0])
  71. class ObjectDeletedError(sa_exc.InvalidRequestError):
  72. """A refresh operation failed to retrieve the database
  73. row corresponding to an object's known primary key identity.
  74. A refresh operation proceeds when an expired attribute is
  75. accessed on an object, or when :meth:`.Query.get` is
  76. used to retrieve an object which is, upon retrieval, detected
  77. as expired. A SELECT is emitted for the target row
  78. based on primary key; if no row is returned, this
  79. exception is raised.
  80. The true meaning of this exception is simply that
  81. no row exists for the primary key identifier associated
  82. with a persistent object. The row may have been
  83. deleted, or in some cases the primary key updated
  84. to a new value, outside of the ORM's management of the target
  85. object.
  86. """
  87. @util.dependencies("sqlalchemy.orm.base")
  88. def __init__(self, base, state, msg=None):
  89. if not msg:
  90. msg = "Instance '%s' has been deleted, or its "\
  91. "row is otherwise not present." % base.state_str(state)
  92. sa_exc.InvalidRequestError.__init__(self, msg)
  93. def __reduce__(self):
  94. return self.__class__, (None, self.args[0])
  95. class UnmappedColumnError(sa_exc.InvalidRequestError):
  96. """Mapping operation was requested on an unknown column."""
  97. class NoResultFound(sa_exc.InvalidRequestError):
  98. """A database result was required but none was found."""
  99. class MultipleResultsFound(sa_exc.InvalidRequestError):
  100. """A single database result was required but more than one were found."""
  101. def _safe_cls_name(cls):
  102. try:
  103. cls_name = '.'.join((cls.__module__, cls.__name__))
  104. except AttributeError:
  105. cls_name = getattr(cls, '__name__', None)
  106. if cls_name is None:
  107. cls_name = repr(cls)
  108. return cls_name
  109. @util.dependencies("sqlalchemy.orm.base")
  110. def _default_unmapped(base, cls):
  111. try:
  112. mappers = base.manager_of_class(cls).mappers
  113. except NO_STATE:
  114. mappers = {}
  115. except TypeError:
  116. mappers = {}
  117. name = _safe_cls_name(cls)
  118. if not mappers:
  119. return "Class '%s' is not mapped" % name