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