Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/util.py: 29%

601 statements  

« prev     ^ index     » next       coverage.py v7.0.1, created at 2022-12-25 06:11 +0000

1# orm/util.py 

2# Copyright (C) 2005-2022 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: https://www.opensource.org/licenses/mit-license.php 

7 

8 

9import re 

10import types 

11import weakref 

12 

13from . import attributes # noqa 

14from .base import _class_to_mapper # noqa 

15from .base import _never_set # noqa 

16from .base import _none_set # noqa 

17from .base import attribute_str # noqa 

18from .base import class_mapper # noqa 

19from .base import InspectionAttr # noqa 

20from .base import instance_str # noqa 

21from .base import object_mapper # noqa 

22from .base import object_state # noqa 

23from .base import state_attribute_str # noqa 

24from .base import state_class_str # noqa 

25from .base import state_str # noqa 

26from .interfaces import CriteriaOption 

27from .interfaces import MapperProperty # noqa 

28from .interfaces import ORMColumnsClauseRole 

29from .interfaces import ORMEntityColumnsClauseRole 

30from .interfaces import ORMFromClauseRole 

31from .interfaces import PropComparator # noqa 

32from .path_registry import PathRegistry # noqa 

33from .. import event 

34from .. import exc as sa_exc 

35from .. import inspection 

36from .. import sql 

37from .. import util 

38from ..engine.result import result_tuple 

39from ..sql import base as sql_base 

40from ..sql import coercions 

41from ..sql import expression 

42from ..sql import lambdas 

43from ..sql import roles 

44from ..sql import util as sql_util 

45from ..sql import visitors 

46from ..sql.annotation import SupportsCloneAnnotations 

47from ..sql.base import ColumnCollection 

48 

49 

50all_cascades = frozenset( 

51 ( 

52 "delete", 

53 "delete-orphan", 

54 "all", 

55 "merge", 

56 "expunge", 

57 "save-update", 

58 "refresh-expire", 

59 "none", 

60 ) 

61) 

62 

63 

64class CascadeOptions(frozenset): 

65 """Keeps track of the options sent to 

66 :paramref:`.relationship.cascade`""" 

67 

68 _add_w_all_cascades = all_cascades.difference( 

69 ["all", "none", "delete-orphan"] 

70 ) 

71 _allowed_cascades = all_cascades 

72 

73 _viewonly_cascades = ["expunge", "all", "none", "refresh-expire", "merge"] 

74 

75 __slots__ = ( 

76 "save_update", 

77 "delete", 

78 "refresh_expire", 

79 "merge", 

80 "expunge", 

81 "delete_orphan", 

82 ) 

83 

84 def __new__(cls, value_list): 

85 if isinstance(value_list, util.string_types) or value_list is None: 

86 return cls.from_string(value_list) 

87 values = set(value_list) 

88 if values.difference(cls._allowed_cascades): 

89 raise sa_exc.ArgumentError( 

90 "Invalid cascade option(s): %s" 

91 % ", ".join( 

92 [ 

93 repr(x) 

94 for x in sorted( 

95 values.difference(cls._allowed_cascades) 

96 ) 

97 ] 

98 ) 

99 ) 

100 

101 if "all" in values: 

102 values.update(cls._add_w_all_cascades) 

103 if "none" in values: 

104 values.clear() 

105 values.discard("all") 

106 

107 self = frozenset.__new__(CascadeOptions, values) 

108 self.save_update = "save-update" in values 

109 self.delete = "delete" in values 

110 self.refresh_expire = "refresh-expire" in values 

111 self.merge = "merge" in values 

112 self.expunge = "expunge" in values 

113 self.delete_orphan = "delete-orphan" in values 

114 

115 if self.delete_orphan and not self.delete: 

116 util.warn( 

117 "The 'delete-orphan' cascade " "option requires 'delete'." 

118 ) 

119 return self 

120 

121 def __repr__(self): 

122 return "CascadeOptions(%r)" % (",".join([x for x in sorted(self)])) 

123 

124 @classmethod 

125 def from_string(cls, arg): 

126 values = [c for c in re.split(r"\s*,\s*", arg or "") if c] 

127 return cls(values) 

128 

129 

130def _validator_events(desc, key, validator, include_removes, include_backrefs): 

131 """Runs a validation method on an attribute value to be set or 

132 appended. 

133 """ 

134 

135 if not include_backrefs: 

136 

137 def detect_is_backref(state, initiator): 

138 impl = state.manager[key].impl 

139 return initiator.impl is not impl 

140 

141 if include_removes: 

142 

143 def append(state, value, initiator): 

144 if initiator.op is not attributes.OP_BULK_REPLACE and ( 

145 include_backrefs or not detect_is_backref(state, initiator) 

146 ): 

147 return validator(state.obj(), key, value, False) 

148 else: 

149 return value 

150 

151 def bulk_set(state, values, initiator): 

152 if include_backrefs or not detect_is_backref(state, initiator): 

153 obj = state.obj() 

154 values[:] = [ 

155 validator(obj, key, value, False) for value in values 

156 ] 

157 

158 def set_(state, value, oldvalue, initiator): 

159 if include_backrefs or not detect_is_backref(state, initiator): 

160 return validator(state.obj(), key, value, False) 

161 else: 

162 return value 

163 

164 def remove(state, value, initiator): 

165 if include_backrefs or not detect_is_backref(state, initiator): 

166 validator(state.obj(), key, value, True) 

167 

168 else: 

169 

170 def append(state, value, initiator): 

171 if initiator.op is not attributes.OP_BULK_REPLACE and ( 

172 include_backrefs or not detect_is_backref(state, initiator) 

173 ): 

174 return validator(state.obj(), key, value) 

175 else: 

176 return value 

177 

178 def bulk_set(state, values, initiator): 

179 if include_backrefs or not detect_is_backref(state, initiator): 

180 obj = state.obj() 

181 values[:] = [validator(obj, key, value) for value in values] 

182 

183 def set_(state, value, oldvalue, initiator): 

184 if include_backrefs or not detect_is_backref(state, initiator): 

185 return validator(state.obj(), key, value) 

186 else: 

187 return value 

188 

189 event.listen(desc, "append", append, raw=True, retval=True) 

190 event.listen(desc, "bulk_replace", bulk_set, raw=True) 

191 event.listen(desc, "set", set_, raw=True, retval=True) 

192 if include_removes: 

193 event.listen(desc, "remove", remove, raw=True, retval=True) 

194 

195 

196def polymorphic_union( 

197 table_map, typecolname, aliasname="p_union", cast_nulls=True 

198): 

199 """Create a ``UNION`` statement used by a polymorphic mapper. 

200 

201 See :ref:`concrete_inheritance` for an example of how 

202 this is used. 

203 

204 :param table_map: mapping of polymorphic identities to 

205 :class:`_schema.Table` objects. 

206 :param typecolname: string name of a "discriminator" column, which will be 

207 derived from the query, producing the polymorphic identity for 

208 each row. If ``None``, no polymorphic discriminator is generated. 

209 :param aliasname: name of the :func:`~sqlalchemy.sql.expression.alias()` 

210 construct generated. 

211 :param cast_nulls: if True, non-existent columns, which are represented 

212 as labeled NULLs, will be passed into CAST. This is a legacy behavior 

213 that is problematic on some backends such as Oracle - in which case it 

214 can be set to False. 

215 

216 """ 

217 

218 colnames = util.OrderedSet() 

219 colnamemaps = {} 

220 types = {} 

221 for key in table_map: 

222 table = table_map[key] 

223 

224 table = coercions.expect( 

225 roles.StrictFromClauseRole, table, allow_select=True 

226 ) 

227 table_map[key] = table 

228 

229 m = {} 

230 for c in table.c: 

231 if c.key == typecolname: 

232 raise sa_exc.InvalidRequestError( 

233 "Polymorphic union can't use '%s' as the discriminator " 

234 "column due to mapped column %r; please apply the " 

235 "'typecolname' " 

236 "argument; this is available on " 

237 "ConcreteBase as '_concrete_discriminator_name'" 

238 % (typecolname, c) 

239 ) 

240 colnames.add(c.key) 

241 m[c.key] = c 

242 types[c.key] = c.type 

243 colnamemaps[table] = m 

244 

245 def col(name, table): 

246 try: 

247 return colnamemaps[table][name] 

248 except KeyError: 

249 if cast_nulls: 

250 return sql.cast(sql.null(), types[name]).label(name) 

251 else: 

252 return sql.type_coerce(sql.null(), types[name]).label(name) 

253 

254 result = [] 

255 for type_, table in table_map.items(): 

256 if typecolname is not None: 

257 result.append( 

258 sql.select( 

259 *( 

260 [col(name, table) for name in colnames] 

261 + [ 

262 sql.literal_column( 

263 sql_util._quote_ddl_expr(type_) 

264 ).label(typecolname) 

265 ] 

266 ) 

267 ).select_from(table) 

268 ) 

269 else: 

270 result.append( 

271 sql.select( 

272 *[col(name, table) for name in colnames] 

273 ).select_from(table) 

274 ) 

275 return sql.union_all(*result).alias(aliasname) 

276 

277 

278def identity_key(*args, **kwargs): 

279 r"""Generate "identity key" tuples, as are used as keys in the 

280 :attr:`.Session.identity_map` dictionary. 

281 

282 This function has several call styles: 

283 

284 * ``identity_key(class, ident, identity_token=token)`` 

285 

286 This form receives a mapped class and a primary key scalar or 

287 tuple as an argument. 

288 

289 E.g.:: 

290 

291 >>> identity_key(MyClass, (1, 2)) 

292 (<class '__main__.MyClass'>, (1, 2), None) 

293 

294 :param class: mapped class (must be a positional argument) 

295 :param ident: primary key, may be a scalar or tuple argument. 

296 :param identity_token: optional identity token 

297 

298 .. versionadded:: 1.2 added identity_token 

299 

300 

301 * ``identity_key(instance=instance)`` 

302 

303 This form will produce the identity key for a given instance. The 

304 instance need not be persistent, only that its primary key attributes 

305 are populated (else the key will contain ``None`` for those missing 

306 values). 

307 

308 E.g.:: 

309 

310 >>> instance = MyClass(1, 2) 

311 >>> identity_key(instance=instance) 

312 (<class '__main__.MyClass'>, (1, 2), None) 

313 

314 In this form, the given instance is ultimately run though 

315 :meth:`_orm.Mapper.identity_key_from_instance`, which will have the 

316 effect of performing a database check for the corresponding row 

317 if the object is expired. 

318 

319 :param instance: object instance (must be given as a keyword arg) 

320 

321 * ``identity_key(class, row=row, identity_token=token)`` 

322 

323 This form is similar to the class/tuple form, except is passed a 

324 database result row as a :class:`.Row` object. 

325 

326 E.g.:: 

327 

328 >>> row = engine.execute(\ 

329 text("select * from table where a=1 and b=2")\ 

330 ).first() 

331 >>> identity_key(MyClass, row=row) 

332 (<class '__main__.MyClass'>, (1, 2), None) 

333 

334 :param class: mapped class (must be a positional argument) 

335 :param row: :class:`.Row` row returned by a :class:`_engine.CursorResult` 

336 (must be given as a keyword arg) 

337 :param identity_token: optional identity token 

338 

339 .. versionadded:: 1.2 added identity_token 

340 

341 """ 

342 if args: 

343 row = None 

344 largs = len(args) 

345 if largs == 1: 

346 class_ = args[0] 

347 try: 

348 row = kwargs.pop("row") 

349 except KeyError: 

350 ident = kwargs.pop("ident") 

351 elif largs in (2, 3): 

352 class_, ident = args 

353 else: 

354 raise sa_exc.ArgumentError( 

355 "expected up to three positional arguments, " "got %s" % largs 

356 ) 

357 

358 identity_token = kwargs.pop("identity_token", None) 

359 if kwargs: 

360 raise sa_exc.ArgumentError( 

361 "unknown keyword arguments: %s" % ", ".join(kwargs) 

362 ) 

363 mapper = class_mapper(class_) 

364 if row is None: 

365 return mapper.identity_key_from_primary_key( 

366 util.to_list(ident), identity_token=identity_token 

367 ) 

368 else: 

369 return mapper.identity_key_from_row( 

370 row, identity_token=identity_token 

371 ) 

372 else: 

373 instance = kwargs.pop("instance") 

374 if kwargs: 

375 raise sa_exc.ArgumentError( 

376 "unknown keyword arguments: %s" % ", ".join(kwargs.keys) 

377 ) 

378 mapper = object_mapper(instance) 

379 return mapper.identity_key_from_instance(instance) 

380 

381 

382class ORMAdapter(sql_util.ColumnAdapter): 

383 """ColumnAdapter subclass which excludes adaptation of entities from 

384 non-matching mappers. 

385 

386 """ 

387 

388 def __init__( 

389 self, 

390 entity, 

391 equivalents=None, 

392 adapt_required=False, 

393 allow_label_resolve=True, 

394 anonymize_labels=False, 

395 ): 

396 info = inspection.inspect(entity) 

397 

398 self.mapper = info.mapper 

399 selectable = info.selectable 

400 is_aliased_class = info.is_aliased_class 

401 if is_aliased_class: 

402 self.aliased_class = entity 

403 else: 

404 self.aliased_class = None 

405 

406 sql_util.ColumnAdapter.__init__( 

407 self, 

408 selectable, 

409 equivalents, 

410 adapt_required=adapt_required, 

411 allow_label_resolve=allow_label_resolve, 

412 anonymize_labels=anonymize_labels, 

413 include_fn=self._include_fn, 

414 ) 

415 

416 def _include_fn(self, elem): 

417 entity = elem._annotations.get("parentmapper", None) 

418 

419 return not entity or entity.isa(self.mapper) or self.mapper.isa(entity) 

420 

421 

422class AliasedClass(object): 

423 r"""Represents an "aliased" form of a mapped class for usage with Query. 

424 

425 The ORM equivalent of a :func:`~sqlalchemy.sql.expression.alias` 

426 construct, this object mimics the mapped class using a 

427 ``__getattr__`` scheme and maintains a reference to a 

428 real :class:`~sqlalchemy.sql.expression.Alias` object. 

429 

430 A primary purpose of :class:`.AliasedClass` is to serve as an alternate 

431 within a SQL statement generated by the ORM, such that an existing 

432 mapped entity can be used in multiple contexts. A simple example:: 

433 

434 # find all pairs of users with the same name 

435 user_alias = aliased(User) 

436 session.query(User, user_alias).\ 

437 join((user_alias, User.id > user_alias.id)).\ 

438 filter(User.name == user_alias.name) 

439 

440 :class:`.AliasedClass` is also capable of mapping an existing mapped 

441 class to an entirely new selectable, provided this selectable is column- 

442 compatible with the existing mapped selectable, and it can also be 

443 configured in a mapping as the target of a :func:`_orm.relationship`. 

444 See the links below for examples. 

445 

446 The :class:`.AliasedClass` object is constructed typically using the 

447 :func:`_orm.aliased` function. It also is produced with additional 

448 configuration when using the :func:`_orm.with_polymorphic` function. 

449 

450 The resulting object is an instance of :class:`.AliasedClass`. 

451 This object implements an attribute scheme which produces the 

452 same attribute and method interface as the original mapped 

453 class, allowing :class:`.AliasedClass` to be compatible 

454 with any attribute technique which works on the original class, 

455 including hybrid attributes (see :ref:`hybrids_toplevel`). 

456 

457 The :class:`.AliasedClass` can be inspected for its underlying 

458 :class:`_orm.Mapper`, aliased selectable, and other information 

459 using :func:`_sa.inspect`:: 

460 

461 from sqlalchemy import inspect 

462 my_alias = aliased(MyClass) 

463 insp = inspect(my_alias) 

464 

465 The resulting inspection object is an instance of :class:`.AliasedInsp`. 

466 

467 

468 .. seealso:: 

469 

470 :func:`.aliased` 

471 

472 :func:`.with_polymorphic` 

473 

474 :ref:`relationship_aliased_class` 

475 

476 :ref:`relationship_to_window_function` 

477 

478 

479 """ 

480 

481 def __init__( 

482 self, 

483 mapped_class_or_ac, 

484 alias=None, 

485 name=None, 

486 flat=False, 

487 adapt_on_names=False, 

488 # TODO: None for default here? 

489 with_polymorphic_mappers=(), 

490 with_polymorphic_discriminator=None, 

491 base_alias=None, 

492 use_mapper_path=False, 

493 represents_outer_join=False, 

494 ): 

495 insp = inspection.inspect(mapped_class_or_ac) 

496 mapper = insp.mapper 

497 

498 nest_adapters = False 

499 

500 if alias is None: 

501 if insp.is_aliased_class and insp.selectable._is_subquery: 

502 alias = insp.selectable.alias() 

503 else: 

504 alias = ( 

505 mapper._with_polymorphic_selectable._anonymous_fromclause( 

506 name=name, 

507 flat=flat, 

508 ) 

509 ) 

510 elif insp.is_aliased_class: 

511 nest_adapters = True 

512 

513 self._aliased_insp = AliasedInsp( 

514 self, 

515 insp, 

516 alias, 

517 name, 

518 with_polymorphic_mappers 

519 if with_polymorphic_mappers 

520 else mapper.with_polymorphic_mappers, 

521 with_polymorphic_discriminator 

522 if with_polymorphic_discriminator is not None 

523 else mapper.polymorphic_on, 

524 base_alias, 

525 use_mapper_path, 

526 adapt_on_names, 

527 represents_outer_join, 

528 nest_adapters, 

529 ) 

530 

531 self.__name__ = "AliasedClass_%s" % mapper.class_.__name__ 

532 

533 @classmethod 

534 def _reconstitute_from_aliased_insp(cls, aliased_insp): 

535 obj = cls.__new__(cls) 

536 obj.__name__ = "AliasedClass_%s" % aliased_insp.mapper.class_.__name__ 

537 obj._aliased_insp = aliased_insp 

538 

539 if aliased_insp._is_with_polymorphic: 

540 for sub_aliased_insp in aliased_insp._with_polymorphic_entities: 

541 if sub_aliased_insp is not aliased_insp: 

542 ent = AliasedClass._reconstitute_from_aliased_insp( 

543 sub_aliased_insp 

544 ) 

545 setattr(obj, sub_aliased_insp.class_.__name__, ent) 

546 

547 return obj 

548 

549 def __getattr__(self, key): 

550 try: 

551 _aliased_insp = self.__dict__["_aliased_insp"] 

552 except KeyError: 

553 raise AttributeError() 

554 else: 

555 target = _aliased_insp._target 

556 # maintain all getattr mechanics 

557 attr = getattr(target, key) 

558 

559 # attribute is a method, that will be invoked against a 

560 # "self"; so just return a new method with the same function and 

561 # new self 

562 if hasattr(attr, "__call__") and hasattr(attr, "__self__"): 

563 return types.MethodType(attr.__func__, self) 

564 

565 # attribute is a descriptor, that will be invoked against a 

566 # "self"; so invoke the descriptor against this self 

567 if hasattr(attr, "__get__"): 

568 attr = attr.__get__(None, self) 

569 

570 # attributes within the QueryableAttribute system will want this 

571 # to be invoked so the object can be adapted 

572 if hasattr(attr, "adapt_to_entity"): 

573 attr = attr.adapt_to_entity(_aliased_insp) 

574 setattr(self, key, attr) 

575 

576 return attr 

577 

578 def _get_from_serialized(self, key, mapped_class, aliased_insp): 

579 # this method is only used in terms of the 

580 # sqlalchemy.ext.serializer extension 

581 attr = getattr(mapped_class, key) 

582 if hasattr(attr, "__call__") and hasattr(attr, "__self__"): 

583 return types.MethodType(attr.__func__, self) 

584 

585 # attribute is a descriptor, that will be invoked against a 

586 # "self"; so invoke the descriptor against this self 

587 if hasattr(attr, "__get__"): 

588 attr = attr.__get__(None, self) 

589 

590 # attributes within the QueryableAttribute system will want this 

591 # to be invoked so the object can be adapted 

592 if hasattr(attr, "adapt_to_entity"): 

593 aliased_insp._weak_entity = weakref.ref(self) 

594 attr = attr.adapt_to_entity(aliased_insp) 

595 setattr(self, key, attr) 

596 

597 return attr 

598 

599 def __repr__(self): 

600 return "<AliasedClass at 0x%x; %s>" % ( 

601 id(self), 

602 self._aliased_insp._target.__name__, 

603 ) 

604 

605 def __str__(self): 

606 return str(self._aliased_insp) 

607 

608 

609class AliasedInsp( 

610 ORMEntityColumnsClauseRole, 

611 ORMFromClauseRole, 

612 sql_base.MemoizedHasCacheKey, 

613 InspectionAttr, 

614): 

615 """Provide an inspection interface for an 

616 :class:`.AliasedClass` object. 

617 

618 The :class:`.AliasedInsp` object is returned 

619 given an :class:`.AliasedClass` using the 

620 :func:`_sa.inspect` function:: 

621 

622 from sqlalchemy import inspect 

623 from sqlalchemy.orm import aliased 

624 

625 my_alias = aliased(MyMappedClass) 

626 insp = inspect(my_alias) 

627 

628 Attributes on :class:`.AliasedInsp` 

629 include: 

630 

631 * ``entity`` - the :class:`.AliasedClass` represented. 

632 * ``mapper`` - the :class:`_orm.Mapper` mapping the underlying class. 

633 * ``selectable`` - the :class:`_expression.Alias` 

634 construct which ultimately 

635 represents an aliased :class:`_schema.Table` or 

636 :class:`_expression.Select` 

637 construct. 

638 * ``name`` - the name of the alias. Also is used as the attribute 

639 name when returned in a result tuple from :class:`_query.Query`. 

640 * ``with_polymorphic_mappers`` - collection of :class:`_orm.Mapper` 

641 objects 

642 indicating all those mappers expressed in the select construct 

643 for the :class:`.AliasedClass`. 

644 * ``polymorphic_on`` - an alternate column or SQL expression which 

645 will be used as the "discriminator" for a polymorphic load. 

646 

647 .. seealso:: 

648 

649 :ref:`inspection_toplevel` 

650 

651 """ 

652 

653 _cache_key_traversal = [ 

654 ("name", visitors.ExtendedInternalTraversal.dp_string), 

655 ("_adapt_on_names", visitors.ExtendedInternalTraversal.dp_boolean), 

656 ("_use_mapper_path", visitors.ExtendedInternalTraversal.dp_boolean), 

657 ("_target", visitors.ExtendedInternalTraversal.dp_inspectable), 

658 ("selectable", visitors.ExtendedInternalTraversal.dp_clauseelement), 

659 ( 

660 "with_polymorphic_mappers", 

661 visitors.InternalTraversal.dp_has_cache_key_list, 

662 ), 

663 ("polymorphic_on", visitors.InternalTraversal.dp_clauseelement), 

664 ] 

665 

666 def __init__( 

667 self, 

668 entity, 

669 inspected, 

670 selectable, 

671 name, 

672 with_polymorphic_mappers, 

673 polymorphic_on, 

674 _base_alias, 

675 _use_mapper_path, 

676 adapt_on_names, 

677 represents_outer_join, 

678 nest_adapters, 

679 ): 

680 

681 mapped_class_or_ac = inspected.entity 

682 mapper = inspected.mapper 

683 

684 self._weak_entity = weakref.ref(entity) 

685 self.mapper = mapper 

686 self.selectable = ( 

687 self.persist_selectable 

688 ) = self.local_table = selectable 

689 self.name = name 

690 self.polymorphic_on = polymorphic_on 

691 self._base_alias = weakref.ref(_base_alias or self) 

692 self._use_mapper_path = _use_mapper_path 

693 self.represents_outer_join = represents_outer_join 

694 self._nest_adapters = nest_adapters 

695 

696 if with_polymorphic_mappers: 

697 self._is_with_polymorphic = True 

698 self.with_polymorphic_mappers = with_polymorphic_mappers 

699 self._with_polymorphic_entities = [] 

700 for poly in self.with_polymorphic_mappers: 

701 if poly is not mapper: 

702 ent = AliasedClass( 

703 poly.class_, 

704 selectable, 

705 base_alias=self, 

706 adapt_on_names=adapt_on_names, 

707 use_mapper_path=_use_mapper_path, 

708 ) 

709 

710 setattr(self.entity, poly.class_.__name__, ent) 

711 self._with_polymorphic_entities.append(ent._aliased_insp) 

712 

713 else: 

714 self._is_with_polymorphic = False 

715 self.with_polymorphic_mappers = [mapper] 

716 

717 self._adapter = sql_util.ColumnAdapter( 

718 selectable, 

719 equivalents=mapper._equivalent_columns, 

720 adapt_on_names=adapt_on_names, 

721 anonymize_labels=True, 

722 # make sure the adapter doesn't try to grab other tables that 

723 # are not even the thing we are mapping, such as embedded 

724 # selectables in subqueries or CTEs. See issue #6060 

725 adapt_from_selectables={ 

726 m.selectable 

727 for m in self.with_polymorphic_mappers 

728 if not adapt_on_names 

729 }, 

730 ) 

731 

732 if nest_adapters: 

733 self._adapter = inspected._adapter.wrap(self._adapter) 

734 

735 self._adapt_on_names = adapt_on_names 

736 self._target = mapped_class_or_ac 

737 # self._target = mapper.class_ # mapped_class_or_ac 

738 

739 @property 

740 def entity(self): 

741 # to eliminate reference cycles, the AliasedClass is held weakly. 

742 # this produces some situations where the AliasedClass gets lost, 

743 # particularly when one is created internally and only the AliasedInsp 

744 # is passed around. 

745 # to work around this case, we just generate a new one when we need 

746 # it, as it is a simple class with very little initial state on it. 

747 ent = self._weak_entity() 

748 if ent is None: 

749 ent = AliasedClass._reconstitute_from_aliased_insp(self) 

750 self._weak_entity = weakref.ref(ent) 

751 return ent 

752 

753 is_aliased_class = True 

754 "always returns True" 

755 

756 @util.memoized_instancemethod 

757 def __clause_element__(self): 

758 return self.selectable._annotate( 

759 { 

760 "parentmapper": self.mapper, 

761 "parententity": self, 

762 "entity_namespace": self, 

763 } 

764 )._set_propagate_attrs( 

765 {"compile_state_plugin": "orm", "plugin_subject": self} 

766 ) 

767 

768 @property 

769 def entity_namespace(self): 

770 return self.entity 

771 

772 @property 

773 def class_(self): 

774 """Return the mapped class ultimately represented by this 

775 :class:`.AliasedInsp`.""" 

776 return self.mapper.class_ 

777 

778 @property 

779 def _path_registry(self): 

780 if self._use_mapper_path: 

781 return self.mapper._path_registry 

782 else: 

783 return PathRegistry.per_mapper(self) 

784 

785 def __getstate__(self): 

786 return { 

787 "entity": self.entity, 

788 "mapper": self.mapper, 

789 "alias": self.selectable, 

790 "name": self.name, 

791 "adapt_on_names": self._adapt_on_names, 

792 "with_polymorphic_mappers": self.with_polymorphic_mappers, 

793 "with_polymorphic_discriminator": self.polymorphic_on, 

794 "base_alias": self._base_alias(), 

795 "use_mapper_path": self._use_mapper_path, 

796 "represents_outer_join": self.represents_outer_join, 

797 "nest_adapters": self._nest_adapters, 

798 } 

799 

800 def __setstate__(self, state): 

801 self.__init__( 

802 state["entity"], 

803 state["mapper"], 

804 state["alias"], 

805 state["name"], 

806 state["with_polymorphic_mappers"], 

807 state["with_polymorphic_discriminator"], 

808 state["base_alias"], 

809 state["use_mapper_path"], 

810 state["adapt_on_names"], 

811 state["represents_outer_join"], 

812 state["nest_adapters"], 

813 ) 

814 

815 def _adapt_element(self, elem, key=None): 

816 d = { 

817 "parententity": self, 

818 "parentmapper": self.mapper, 

819 } 

820 if key: 

821 d["proxy_key"] = key 

822 return ( 

823 self._adapter.traverse(elem) 

824 ._annotate(d) 

825 ._set_propagate_attrs( 

826 {"compile_state_plugin": "orm", "plugin_subject": self} 

827 ) 

828 ) 

829 

830 def _entity_for_mapper(self, mapper): 

831 self_poly = self.with_polymorphic_mappers 

832 if mapper in self_poly: 

833 if mapper is self.mapper: 

834 return self 

835 else: 

836 return getattr( 

837 self.entity, mapper.class_.__name__ 

838 )._aliased_insp 

839 elif mapper.isa(self.mapper): 

840 return self 

841 else: 

842 assert False, "mapper %s doesn't correspond to %s" % (mapper, self) 

843 

844 @util.memoized_property 

845 def _get_clause(self): 

846 onclause, replacemap = self.mapper._get_clause 

847 return ( 

848 self._adapter.traverse(onclause), 

849 { 

850 self._adapter.traverse(col): param 

851 for col, param in replacemap.items() 

852 }, 

853 ) 

854 

855 @util.memoized_property 

856 def _memoized_values(self): 

857 return {} 

858 

859 @util.memoized_property 

860 def _all_column_expressions(self): 

861 if self._is_with_polymorphic: 

862 cols_plus_keys = self.mapper._columns_plus_keys( 

863 [ent.mapper for ent in self._with_polymorphic_entities] 

864 ) 

865 else: 

866 cols_plus_keys = self.mapper._columns_plus_keys() 

867 

868 cols_plus_keys = [ 

869 (key, self._adapt_element(col)) for key, col in cols_plus_keys 

870 ] 

871 

872 return ColumnCollection(cols_plus_keys) 

873 

874 def _memo(self, key, callable_, *args, **kw): 

875 if key in self._memoized_values: 

876 return self._memoized_values[key] 

877 else: 

878 self._memoized_values[key] = value = callable_(*args, **kw) 

879 return value 

880 

881 def __repr__(self): 

882 if self.with_polymorphic_mappers: 

883 with_poly = "(%s)" % ", ".join( 

884 mp.class_.__name__ for mp in self.with_polymorphic_mappers 

885 ) 

886 else: 

887 with_poly = "" 

888 return "<AliasedInsp at 0x%x; %s%s>" % ( 

889 id(self), 

890 self.class_.__name__, 

891 with_poly, 

892 ) 

893 

894 def __str__(self): 

895 if self._is_with_polymorphic: 

896 return "with_polymorphic(%s, [%s])" % ( 

897 self._target.__name__, 

898 ", ".join( 

899 mp.class_.__name__ 

900 for mp in self.with_polymorphic_mappers 

901 if mp is not self.mapper 

902 ), 

903 ) 

904 else: 

905 return "aliased(%s)" % (self._target.__name__,) 

906 

907 

908class _WrapUserEntity(object): 

909 """A wrapper used within the loader_criteria lambda caller so that 

910 we can bypass declared_attr descriptors on unmapped mixins, which 

911 normally emit a warning for such use. 

912 

913 might also be useful for other per-lambda instrumentations should 

914 the need arise. 

915 

916 """ 

917 

918 __slots__ = ("subject",) 

919 

920 def __init__(self, subject): 

921 self.subject = subject 

922 

923 @util.preload_module("sqlalchemy.orm.decl_api") 

924 def __getattribute__(self, name): 

925 decl_api = util.preloaded.orm.decl_api 

926 

927 subject = object.__getattribute__(self, "subject") 

928 if name in subject.__dict__ and isinstance( 

929 subject.__dict__[name], decl_api.declared_attr 

930 ): 

931 return subject.__dict__[name].fget(subject) 

932 else: 

933 return getattr(subject, name) 

934 

935 

936class LoaderCriteriaOption(CriteriaOption): 

937 """Add additional WHERE criteria to the load for all occurrences of 

938 a particular entity. 

939 

940 :class:`_orm.LoaderCriteriaOption` is invoked using the 

941 :func:`_orm.with_loader_criteria` function; see that function for 

942 details. 

943 

944 .. versionadded:: 1.4 

945 

946 """ 

947 

948 _traverse_internals = [ 

949 ("root_entity", visitors.ExtendedInternalTraversal.dp_plain_obj), 

950 ("entity", visitors.ExtendedInternalTraversal.dp_has_cache_key), 

951 ("where_criteria", visitors.InternalTraversal.dp_clauseelement), 

952 ("include_aliases", visitors.InternalTraversal.dp_boolean), 

953 ("propagate_to_loaders", visitors.InternalTraversal.dp_boolean), 

954 ] 

955 

956 def __init__( 

957 self, 

958 entity_or_base, 

959 where_criteria, 

960 loader_only=False, 

961 include_aliases=False, 

962 propagate_to_loaders=True, 

963 track_closure_variables=True, 

964 ): 

965 """Add additional WHERE criteria to the load for all occurrences of 

966 a particular entity. 

967 

968 .. versionadded:: 1.4 

969 

970 The :func:`_orm.with_loader_criteria` option is intended to add 

971 limiting criteria to a particular kind of entity in a query, 

972 **globally**, meaning it will apply to the entity as it appears 

973 in the SELECT query as well as within any subqueries, join 

974 conditions, and relationship loads, including both eager and lazy 

975 loaders, without the need for it to be specified in any particular 

976 part of the query. The rendering logic uses the same system used by 

977 single table inheritance to ensure a certain discriminator is applied 

978 to a table. 

979 

980 E.g., using :term:`2.0-style` queries, we can limit the way the 

981 ``User.addresses`` collection is loaded, regardless of the kind 

982 of loading used:: 

983 

984 from sqlalchemy.orm import with_loader_criteria 

985 

986 stmt = select(User).options( 

987 selectinload(User.addresses), 

988 with_loader_criteria(Address, Address.email_address != 'foo')) 

989 ) 

990 

991 Above, the "selectinload" for ``User.addresses`` will apply the 

992 given filtering criteria to the WHERE clause. 

993 

994 Another example, where the filtering will be applied to the 

995 ON clause of the join, in this example using :term:`1.x style` 

996 queries:: 

997 

998 q = session.query(User).outerjoin(User.addresses).options( 

999 with_loader_criteria(Address, Address.email_address != 'foo')) 

1000 ) 

1001 

1002 The primary purpose of :func:`_orm.with_loader_criteria` is to use 

1003 it in the :meth:`_orm.SessionEvents.do_orm_execute` event handler 

1004 to ensure that all occurrences of a particular entity are filtered 

1005 in a certain way, such as filtering for access control roles. It 

1006 also can be used to apply criteria to relationship loads. In the 

1007 example below, we can apply a certain set of rules to all queries 

1008 emitted by a particular :class:`_orm.Session`:: 

1009 

1010 session = Session(bind=engine) 

1011 

1012 @event.listens_for("do_orm_execute", session) 

1013 def _add_filtering_criteria(execute_state): 

1014 

1015 if ( 

1016 execute_state.is_select 

1017 and not execute_state.is_column_load 

1018 and not execute_state.is_relationship_load 

1019 ): 

1020 execute_state.statement = execute_state.statement.options( 

1021 with_loader_criteria( 

1022 SecurityRole, 

1023 lambda cls: cls.role.in_(['some_role']), 

1024 include_aliases=True 

1025 ) 

1026 ) 

1027 

1028 In the above example, the :meth:`_orm.SessionEvents.do_orm_execute` 

1029 event will intercept all queries emitted using the 

1030 :class:`_orm.Session`. For those queries which are SELECT statements 

1031 and are not attribute or relationship loads a custom 

1032 :func:`_orm.with_loader_criteria` option is added to the query. The 

1033 :func:`_orm.with_loader_criteria` option will be used in the given 

1034 statement and will also be automatically propagated to all relationship 

1035 loads that descend from this query. 

1036 

1037 The criteria argument given is a ``lambda`` that accepts a ``cls`` 

1038 argument. The given class will expand to include all mapped subclass 

1039 and need not itself be a mapped class. 

1040 

1041 .. tip:: 

1042 

1043 When using :func:`_orm.with_loader_criteria` option in 

1044 conjunction with the :func:`_orm.contains_eager` loader option, 

1045 it's important to note that :func:`_orm.with_loader_criteria` only 

1046 affects the part of the query that determines what SQL is rendered 

1047 in terms of the WHERE and FROM clauses. The 

1048 :func:`_orm.contains_eager` option does not affect the rendering of 

1049 the SELECT statement outside of the columns clause, so does not have 

1050 any interaction with the :func:`_orm.with_loader_criteria` option. 

1051 However, the way things "work" is that :func:`_orm.contains_eager` 

1052 is meant to be used with a query that is already selecting from the 

1053 additional entities in some way, where 

1054 :func:`_orm.with_loader_criteria` can apply it's additional 

1055 criteria. 

1056 

1057 In the example below, assuming a mapping relationship as 

1058 ``A -> A.bs -> B``, the given :func:`_orm.with_loader_criteria` 

1059 option will affect the way in which the JOIN is rendered:: 

1060 

1061 stmt = select(A).join(A.bs).options( 

1062 contains_eager(A.bs), 

1063 with_loader_criteria(B, B.flag == 1) 

1064 ) 

1065 

1066 Above, the given :func:`_orm.with_loader_criteria` option will 

1067 affect the ON clause of the JOIN that is specified by 

1068 ``.join(A.bs)``, so is applied as expected. The 

1069 :func:`_orm.contains_eager` option has the effect that columns from 

1070 ``B`` are added to the columns clause:: 

1071 

1072 SELECT 

1073 b.id, b.a_id, b.data, b.flag, 

1074 a.id AS id_1, 

1075 a.data AS data_1 

1076 FROM a JOIN b ON a.id = b.a_id AND b.flag = :flag_1 

1077 

1078 

1079 The use of the :func:`_orm.contains_eager` option within the above 

1080 statement has no effect on the behavior of the 

1081 :func:`_orm.with_loader_criteria` option. If the 

1082 :func:`_orm.contains_eager` option were omitted, the SQL would be 

1083 the same as regards the FROM and WHERE clauses, where 

1084 :func:`_orm.with_loader_criteria` continues to add its criteria to 

1085 the ON clause of the JOIN. The addition of 

1086 :func:`_orm.contains_eager` only affects the columns clause, in that 

1087 additional columns against ``b`` are added which are then consumed 

1088 by the ORM to produce ``B`` instances. 

1089 

1090 .. warning:: The use of a lambda inside of the call to 

1091 :func:`_orm.with_loader_criteria` is only invoked **once per unique 

1092 class**. Custom functions should not be invoked within this lambda. 

1093 See :ref:`engine_lambda_caching` for an overview of the "lambda SQL" 

1094 feature, which is for advanced use only. 

1095 

1096 :param entity_or_base: a mapped class, or a class that is a super 

1097 class of a particular set of mapped classes, to which the rule 

1098 will apply. 

1099 

1100 :param where_criteria: a Core SQL expression that applies limiting 

1101 criteria. This may also be a "lambda:" or Python function that 

1102 accepts a target class as an argument, when the given class is 

1103 a base with many different mapped subclasses. 

1104 

1105 .. note:: To support pickling, use a module-level Python function to 

1106 produce the SQL expression instead of a lambda or a fixed SQL 

1107 expression, which tend to not be picklable. 

1108 

1109 :param include_aliases: if True, apply the rule to :func:`_orm.aliased` 

1110 constructs as well. 

1111 

1112 :param propagate_to_loaders: defaults to True, apply to relationship 

1113 loaders such as lazy loaders. This indicates that the 

1114 option object itself including SQL expression is carried along with 

1115 each loaded instance. Set to ``False`` to prevent the object from 

1116 being assigned to individual instances. 

1117 

1118 .. seealso:: 

1119 

1120 :ref:`examples_session_orm_events` - includes examples of using 

1121 :func:`_orm.with_loader_criteria`. 

1122 

1123 :ref:`do_orm_execute_global_criteria` - basic example on how to 

1124 combine :func:`_orm.with_loader_criteria` with the 

1125 :meth:`_orm.SessionEvents.do_orm_execute` event. 

1126 

1127 :param track_closure_variables: when False, closure variables inside 

1128 of a lambda expression will not be used as part of 

1129 any cache key. This allows more complex expressions to be used 

1130 inside of a lambda expression but requires that the lambda ensures 

1131 it returns the identical SQL every time given a particular class. 

1132 

1133 .. versionadded:: 1.4.0b2 

1134 

1135 """ 

1136 entity = inspection.inspect(entity_or_base, False) 

1137 if entity is None: 

1138 self.root_entity = entity_or_base 

1139 self.entity = None 

1140 else: 

1141 self.root_entity = None 

1142 self.entity = entity 

1143 

1144 self._where_crit_orig = where_criteria 

1145 if callable(where_criteria): 

1146 self.deferred_where_criteria = True 

1147 self.where_criteria = lambdas.DeferredLambdaElement( 

1148 where_criteria, 

1149 roles.WhereHavingRole, 

1150 lambda_args=( 

1151 _WrapUserEntity( 

1152 self.root_entity 

1153 if self.root_entity is not None 

1154 else self.entity.entity, 

1155 ), 

1156 ), 

1157 opts=lambdas.LambdaOptions( 

1158 track_closure_variables=track_closure_variables 

1159 ), 

1160 ) 

1161 else: 

1162 self.deferred_where_criteria = False 

1163 self.where_criteria = coercions.expect( 

1164 roles.WhereHavingRole, where_criteria 

1165 ) 

1166 

1167 self.include_aliases = include_aliases 

1168 self.propagate_to_loaders = propagate_to_loaders 

1169 

1170 @classmethod 

1171 def _unreduce( 

1172 cls, entity, where_criteria, include_aliases, propagate_to_loaders 

1173 ): 

1174 return LoaderCriteriaOption( 

1175 entity, 

1176 where_criteria, 

1177 include_aliases=include_aliases, 

1178 propagate_to_loaders=propagate_to_loaders, 

1179 ) 

1180 

1181 def __reduce__(self): 

1182 return ( 

1183 LoaderCriteriaOption._unreduce, 

1184 ( 

1185 self.entity.class_ if self.entity else self.root_entity, 

1186 self._where_crit_orig, 

1187 self.include_aliases, 

1188 self.propagate_to_loaders, 

1189 ), 

1190 ) 

1191 

1192 def _all_mappers(self): 

1193 

1194 if self.entity: 

1195 for ent in self.entity.mapper.self_and_descendants: 

1196 yield ent 

1197 else: 

1198 stack = list(self.root_entity.__subclasses__()) 

1199 while stack: 

1200 subclass = stack.pop(0) 

1201 ent = inspection.inspect(subclass, raiseerr=False) 

1202 if ent: 

1203 for mp in ent.mapper.self_and_descendants: 

1204 yield mp 

1205 else: 

1206 stack.extend(subclass.__subclasses__()) 

1207 

1208 def _should_include(self, compile_state): 

1209 if ( 

1210 compile_state.select_statement._annotations.get( 

1211 "for_loader_criteria", None 

1212 ) 

1213 is self 

1214 ): 

1215 return False 

1216 return True 

1217 

1218 def _resolve_where_criteria(self, ext_info): 

1219 if self.deferred_where_criteria: 

1220 crit = self.where_criteria._resolve_with_args(ext_info.entity) 

1221 else: 

1222 crit = self.where_criteria 

1223 return sql_util._deep_annotate( 

1224 crit, {"for_loader_criteria": self}, detect_subquery_cols=True 

1225 ) 

1226 

1227 def process_compile_state_replaced_entities( 

1228 self, compile_state, mapper_entities 

1229 ): 

1230 return self.process_compile_state(compile_state) 

1231 

1232 def process_compile_state(self, compile_state): 

1233 """Apply a modification to a given :class:`.CompileState`.""" 

1234 

1235 # if options to limit the criteria to immediate query only, 

1236 # use compile_state.attributes instead 

1237 

1238 if compile_state.compile_options._with_polymorphic_adapt_map: 

1239 util.warn( 

1240 "The with_loader_criteria() function may not work " 

1241 "correctly with the legacy Query.with_polymorphic() feature. " 

1242 "Please migrate code to use the with_polymorphic() standalone " 

1243 "function before using with_loader_criteria()." 

1244 ) 

1245 self.get_global_criteria(compile_state.global_attributes) 

1246 

1247 def get_global_criteria(self, attributes): 

1248 for mp in self._all_mappers(): 

1249 load_criteria = attributes.setdefault( 

1250 ("additional_entity_criteria", mp), [] 

1251 ) 

1252 

1253 load_criteria.append(self) 

1254 

1255 

1256inspection._inspects(AliasedClass)(lambda target: target._aliased_insp) 

1257inspection._inspects(AliasedInsp)(lambda target: target) 

1258 

1259 

1260def aliased(element, alias=None, name=None, flat=False, adapt_on_names=False): 

1261 """Produce an alias of the given element, usually an :class:`.AliasedClass` 

1262 instance. 

1263 

1264 E.g.:: 

1265 

1266 my_alias = aliased(MyClass) 

1267 

1268 session.query(MyClass, my_alias).filter(MyClass.id > my_alias.id) 

1269 

1270 The :func:`.aliased` function is used to create an ad-hoc mapping of a 

1271 mapped class to a new selectable. By default, a selectable is generated 

1272 from the normally mapped selectable (typically a :class:`_schema.Table` 

1273 ) using the 

1274 :meth:`_expression.FromClause.alias` method. However, :func:`.aliased` 

1275 can also be 

1276 used to link the class to a new :func:`_expression.select` statement. 

1277 Also, the :func:`.with_polymorphic` function is a variant of 

1278 :func:`.aliased` that is intended to specify a so-called "polymorphic 

1279 selectable", that corresponds to the union of several joined-inheritance 

1280 subclasses at once. 

1281 

1282 For convenience, the :func:`.aliased` function also accepts plain 

1283 :class:`_expression.FromClause` constructs, such as a 

1284 :class:`_schema.Table` or 

1285 :func:`_expression.select` construct. In those cases, the 

1286 :meth:`_expression.FromClause.alias` 

1287 method is called on the object and the new 

1288 :class:`_expression.Alias` object returned. The returned 

1289 :class:`_expression.Alias` is not 

1290 ORM-mapped in this case. 

1291 

1292 .. seealso:: 

1293 

1294 :ref:`tutorial_orm_entity_aliases` - in the :ref:`unified_tutorial` 

1295 

1296 :ref:`orm_queryguide_orm_aliases` - in the :ref:`queryguide_toplevel` 

1297 

1298 :param element: element to be aliased. Is normally a mapped class, 

1299 but for convenience can also be a :class:`_expression.FromClause` 

1300 element. 

1301 

1302 :param alias: Optional selectable unit to map the element to. This is 

1303 usually used to link the object to a subquery, and should be an aliased 

1304 select construct as one would produce from the 

1305 :meth:`_query.Query.subquery` method or 

1306 the :meth:`_expression.Select.subquery` or 

1307 :meth:`_expression.Select.alias` methods of the :func:`_expression.select` 

1308 construct. 

1309 

1310 :param name: optional string name to use for the alias, if not specified 

1311 by the ``alias`` parameter. The name, among other things, forms the 

1312 attribute name that will be accessible via tuples returned by a 

1313 :class:`_query.Query` object. Not supported when creating aliases 

1314 of :class:`_sql.Join` objects. 

1315 

1316 :param flat: Boolean, will be passed through to the 

1317 :meth:`_expression.FromClause.alias` call so that aliases of 

1318 :class:`_expression.Join` objects will alias the individual tables 

1319 inside the join, rather than creating a subquery. This is generally 

1320 supported by all modern databases with regards to right-nested joins 

1321 and generally produces more efficient queries. 

1322 

1323 :param adapt_on_names: if True, more liberal "matching" will be used when 

1324 mapping the mapped columns of the ORM entity to those of the 

1325 given selectable - a name-based match will be performed if the 

1326 given selectable doesn't otherwise have a column that corresponds 

1327 to one on the entity. The use case for this is when associating 

1328 an entity with some derived selectable such as one that uses 

1329 aggregate functions:: 

1330 

1331 class UnitPrice(Base): 

1332 __tablename__ = 'unit_price' 

1333 ... 

1334 unit_id = Column(Integer) 

1335 price = Column(Numeric) 

1336 

1337 aggregated_unit_price = Session.query( 

1338 func.sum(UnitPrice.price).label('price') 

1339 ).group_by(UnitPrice.unit_id).subquery() 

1340 

1341 aggregated_unit_price = aliased(UnitPrice, 

1342 alias=aggregated_unit_price, adapt_on_names=True) 

1343 

1344 Above, functions on ``aggregated_unit_price`` which refer to 

1345 ``.price`` will return the 

1346 ``func.sum(UnitPrice.price).label('price')`` column, as it is 

1347 matched on the name "price". Ordinarily, the "price" function 

1348 wouldn't have any "column correspondence" to the actual 

1349 ``UnitPrice.price`` column as it is not a proxy of the original. 

1350 

1351 """ 

1352 if isinstance(element, expression.FromClause): 

1353 if adapt_on_names: 

1354 raise sa_exc.ArgumentError( 

1355 "adapt_on_names only applies to ORM elements" 

1356 ) 

1357 if name: 

1358 return element.alias(name=name, flat=flat) 

1359 else: 

1360 return coercions.expect( 

1361 roles.AnonymizedFromClauseRole, element, flat=flat 

1362 ) 

1363 else: 

1364 return AliasedClass( 

1365 element, 

1366 alias=alias, 

1367 flat=flat, 

1368 name=name, 

1369 adapt_on_names=adapt_on_names, 

1370 ) 

1371 

1372 

1373def with_polymorphic( 

1374 base, 

1375 classes, 

1376 selectable=False, 

1377 flat=False, 

1378 polymorphic_on=None, 

1379 aliased=False, 

1380 adapt_on_names=False, 

1381 innerjoin=False, 

1382 _use_mapper_path=False, 

1383 _existing_alias=None, 

1384): 

1385 """Produce an :class:`.AliasedClass` construct which specifies 

1386 columns for descendant mappers of the given base. 

1387 

1388 Using this method will ensure that each descendant mapper's 

1389 tables are included in the FROM clause, and will allow filter() 

1390 criterion to be used against those tables. The resulting 

1391 instances will also have those columns already loaded so that 

1392 no "post fetch" of those columns will be required. 

1393 

1394 .. seealso:: 

1395 

1396 :ref:`with_polymorphic` - full discussion of 

1397 :func:`_orm.with_polymorphic`. 

1398 

1399 :param base: Base class to be aliased. 

1400 

1401 :param classes: a single class or mapper, or list of 

1402 class/mappers, which inherit from the base class. 

1403 Alternatively, it may also be the string ``'*'``, in which case 

1404 all descending mapped classes will be added to the FROM clause. 

1405 

1406 :param aliased: when True, the selectable will be aliased. For a 

1407 JOIN, this means the JOIN will be SELECTed from inside of a subquery 

1408 unless the :paramref:`_orm.with_polymorphic.flat` flag is set to 

1409 True, which is recommended for simpler use cases. 

1410 

1411 :param flat: Boolean, will be passed through to the 

1412 :meth:`_expression.FromClause.alias` call so that aliases of 

1413 :class:`_expression.Join` objects will alias the individual tables 

1414 inside the join, rather than creating a subquery. This is generally 

1415 supported by all modern databases with regards to right-nested joins 

1416 and generally produces more efficient queries. Setting this flag is 

1417 recommended as long as the resulting SQL is functional. 

1418 

1419 :param selectable: a table or subquery that will 

1420 be used in place of the generated FROM clause. This argument is 

1421 required if any of the desired classes use concrete table 

1422 inheritance, since SQLAlchemy currently cannot generate UNIONs 

1423 among tables automatically. If used, the ``selectable`` argument 

1424 must represent the full set of tables and columns mapped by every 

1425 mapped class. Otherwise, the unaccounted mapped columns will 

1426 result in their table being appended directly to the FROM clause 

1427 which will usually lead to incorrect results. 

1428 

1429 When left at its default value of ``False``, the polymorphic 

1430 selectable assigned to the base mapper is used for selecting rows. 

1431 However, it may also be passed as ``None``, which will bypass the 

1432 configured polymorphic selectable and instead construct an ad-hoc 

1433 selectable for the target classes given; for joined table inheritance 

1434 this will be a join that includes all target mappers and their 

1435 subclasses. 

1436 

1437 :param polymorphic_on: a column to be used as the "discriminator" 

1438 column for the given selectable. If not given, the polymorphic_on 

1439 attribute of the base classes' mapper will be used, if any. This 

1440 is useful for mappings that don't have polymorphic loading 

1441 behavior by default. 

1442 

1443 :param innerjoin: if True, an INNER JOIN will be used. This should 

1444 only be specified if querying for one specific subtype only 

1445 

1446 :param adapt_on_names: Passes through the 

1447 :paramref:`_orm.aliased.adapt_on_names` 

1448 parameter to the aliased object. This may be useful in situations where 

1449 the given selectable is not directly related to the existing mapped 

1450 selectable. 

1451 

1452 .. versionadded:: 1.4.33 

1453 

1454 """ 

1455 primary_mapper = _class_to_mapper(base) 

1456 

1457 if selectable not in (None, False) and flat: 

1458 raise sa_exc.ArgumentError( 

1459 "the 'flat' and 'selectable' arguments cannot be passed " 

1460 "simultaneously to with_polymorphic()" 

1461 ) 

1462 

1463 if _existing_alias: 

1464 assert _existing_alias.mapper is primary_mapper 

1465 classes = util.to_set(classes) 

1466 new_classes = set( 

1467 [mp.class_ for mp in _existing_alias.with_polymorphic_mappers] 

1468 ) 

1469 if classes == new_classes: 

1470 return _existing_alias 

1471 else: 

1472 classes = classes.union(new_classes) 

1473 mappers, selectable = primary_mapper._with_polymorphic_args( 

1474 classes, selectable, innerjoin=innerjoin 

1475 ) 

1476 if aliased or flat: 

1477 selectable = selectable._anonymous_fromclause(flat=flat) 

1478 return AliasedClass( 

1479 base, 

1480 selectable, 

1481 adapt_on_names=adapt_on_names, 

1482 with_polymorphic_mappers=mappers, 

1483 with_polymorphic_discriminator=polymorphic_on, 

1484 use_mapper_path=_use_mapper_path, 

1485 represents_outer_join=not innerjoin, 

1486 ) 

1487 

1488 

1489@inspection._self_inspects 

1490class Bundle( 

1491 ORMColumnsClauseRole, 

1492 SupportsCloneAnnotations, 

1493 sql_base.MemoizedHasCacheKey, 

1494 InspectionAttr, 

1495): 

1496 """A grouping of SQL expressions that are returned by a :class:`.Query` 

1497 under one namespace. 

1498 

1499 The :class:`.Bundle` essentially allows nesting of the tuple-based 

1500 results returned by a column-oriented :class:`_query.Query` object. 

1501 It also 

1502 is extensible via simple subclassing, where the primary capability 

1503 to override is that of how the set of expressions should be returned, 

1504 allowing post-processing as well as custom return types, without 

1505 involving ORM identity-mapped classes. 

1506 

1507 .. versionadded:: 0.9.0 

1508 

1509 .. seealso:: 

1510 

1511 :ref:`bundles` 

1512 

1513 

1514 """ 

1515 

1516 single_entity = False 

1517 """If True, queries for a single Bundle will be returned as a single 

1518 entity, rather than an element within a keyed tuple.""" 

1519 

1520 is_clause_element = False 

1521 

1522 is_mapper = False 

1523 

1524 is_aliased_class = False 

1525 

1526 is_bundle = True 

1527 

1528 _propagate_attrs = util.immutabledict() 

1529 

1530 def __init__(self, name, *exprs, **kw): 

1531 r"""Construct a new :class:`.Bundle`. 

1532 

1533 e.g.:: 

1534 

1535 bn = Bundle("mybundle", MyClass.x, MyClass.y) 

1536 

1537 for row in session.query(bn).filter( 

1538 bn.c.x == 5).filter(bn.c.y == 4): 

1539 print(row.mybundle.x, row.mybundle.y) 

1540 

1541 :param name: name of the bundle. 

1542 :param \*exprs: columns or SQL expressions comprising the bundle. 

1543 :param single_entity=False: if True, rows for this :class:`.Bundle` 

1544 can be returned as a "single entity" outside of any enclosing tuple 

1545 in the same manner as a mapped entity. 

1546 

1547 """ 

1548 self.name = self._label = name 

1549 self.exprs = exprs = [ 

1550 coercions.expect( 

1551 roles.ColumnsClauseRole, expr, apply_propagate_attrs=self 

1552 ) 

1553 for expr in exprs 

1554 ] 

1555 

1556 self.c = self.columns = ColumnCollection( 

1557 (getattr(col, "key", col._label), col) 

1558 for col in [e._annotations.get("bundle", e) for e in exprs] 

1559 ) 

1560 self.single_entity = kw.pop("single_entity", self.single_entity) 

1561 

1562 def _gen_cache_key(self, anon_map, bindparams): 

1563 return (self.__class__, self.name, self.single_entity) + tuple( 

1564 [expr._gen_cache_key(anon_map, bindparams) for expr in self.exprs] 

1565 ) 

1566 

1567 @property 

1568 def mapper(self): 

1569 return self.exprs[0]._annotations.get("parentmapper", None) 

1570 

1571 @property 

1572 def entity(self): 

1573 return self.exprs[0]._annotations.get("parententity", None) 

1574 

1575 @property 

1576 def entity_namespace(self): 

1577 return self.c 

1578 

1579 columns = None 

1580 """A namespace of SQL expressions referred to by this :class:`.Bundle`. 

1581 

1582 e.g.:: 

1583 

1584 bn = Bundle("mybundle", MyClass.x, MyClass.y) 

1585 

1586 q = sess.query(bn).filter(bn.c.x == 5) 

1587 

1588 Nesting of bundles is also supported:: 

1589 

1590 b1 = Bundle("b1", 

1591 Bundle('b2', MyClass.a, MyClass.b), 

1592 Bundle('b3', MyClass.x, MyClass.y) 

1593 ) 

1594 

1595 q = sess.query(b1).filter( 

1596 b1.c.b2.c.a == 5).filter(b1.c.b3.c.y == 9) 

1597 

1598 .. seealso:: 

1599 

1600 :attr:`.Bundle.c` 

1601 

1602 """ 

1603 

1604 c = None 

1605 """An alias for :attr:`.Bundle.columns`.""" 

1606 

1607 def _clone(self): 

1608 cloned = self.__class__.__new__(self.__class__) 

1609 cloned.__dict__.update(self.__dict__) 

1610 return cloned 

1611 

1612 def __clause_element__(self): 

1613 # ensure existing entity_namespace remains 

1614 annotations = {"bundle": self, "entity_namespace": self} 

1615 annotations.update(self._annotations) 

1616 

1617 plugin_subject = self.exprs[0]._propagate_attrs.get( 

1618 "plugin_subject", self.entity 

1619 ) 

1620 return ( 

1621 expression.ClauseList( 

1622 _literal_as_text_role=roles.ColumnsClauseRole, 

1623 group=False, 

1624 *[e._annotations.get("bundle", e) for e in self.exprs] 

1625 ) 

1626 ._annotate(annotations) 

1627 ._set_propagate_attrs( 

1628 # the Bundle *must* use the orm plugin no matter what. the 

1629 # subject can be None but it's much better if it's not. 

1630 { 

1631 "compile_state_plugin": "orm", 

1632 "plugin_subject": plugin_subject, 

1633 } 

1634 ) 

1635 ) 

1636 

1637 @property 

1638 def clauses(self): 

1639 return self.__clause_element__().clauses 

1640 

1641 def label(self, name): 

1642 """Provide a copy of this :class:`.Bundle` passing a new label.""" 

1643 

1644 cloned = self._clone() 

1645 cloned.name = name 

1646 return cloned 

1647 

1648 def create_row_processor(self, query, procs, labels): 

1649 """Produce the "row processing" function for this :class:`.Bundle`. 

1650 

1651 May be overridden by subclasses. 

1652 

1653 .. seealso:: 

1654 

1655 :ref:`bundles` - includes an example of subclassing. 

1656 

1657 """ 

1658 keyed_tuple = result_tuple(labels, [() for l in labels]) 

1659 

1660 def proc(row): 

1661 return keyed_tuple([proc(row) for proc in procs]) 

1662 

1663 return proc 

1664 

1665 

1666def _orm_annotate(element, exclude=None): 

1667 """Deep copy the given ClauseElement, annotating each element with the 

1668 "_orm_adapt" flag. 

1669 

1670 Elements within the exclude collection will be cloned but not annotated. 

1671 

1672 """ 

1673 return sql_util._deep_annotate(element, {"_orm_adapt": True}, exclude) 

1674 

1675 

1676def _orm_deannotate(element): 

1677 """Remove annotations that link a column to a particular mapping. 

1678 

1679 Note this doesn't affect "remote" and "foreign" annotations 

1680 passed by the :func:`_orm.foreign` and :func:`_orm.remote` 

1681 annotators. 

1682 

1683 """ 

1684 

1685 return sql_util._deep_deannotate( 

1686 element, values=("_orm_adapt", "parententity") 

1687 ) 

1688 

1689 

1690def _orm_full_deannotate(element): 

1691 return sql_util._deep_deannotate(element) 

1692 

1693 

1694class _ORMJoin(expression.Join): 

1695 """Extend Join to support ORM constructs as input.""" 

1696 

1697 __visit_name__ = expression.Join.__visit_name__ 

1698 

1699 inherit_cache = True 

1700 

1701 def __init__( 

1702 self, 

1703 left, 

1704 right, 

1705 onclause=None, 

1706 isouter=False, 

1707 full=False, 

1708 _left_memo=None, 

1709 _right_memo=None, 

1710 _extra_criteria=(), 

1711 ): 

1712 left_info = inspection.inspect(left) 

1713 

1714 right_info = inspection.inspect(right) 

1715 adapt_to = right_info.selectable 

1716 

1717 # used by joined eager loader 

1718 self._left_memo = _left_memo 

1719 self._right_memo = _right_memo 

1720 

1721 # legacy, for string attr name ON clause. if that's removed 

1722 # then the "_joined_from_info" concept can go 

1723 left_orm_info = getattr(left, "_joined_from_info", left_info) 

1724 self._joined_from_info = right_info 

1725 if isinstance(onclause, util.string_types): 

1726 onclause = getattr(left_orm_info.entity, onclause) 

1727 # #### 

1728 

1729 if isinstance(onclause, attributes.QueryableAttribute): 

1730 on_selectable = onclause.comparator._source_selectable() 

1731 prop = onclause.property 

1732 _extra_criteria += onclause._extra_criteria 

1733 elif isinstance(onclause, MapperProperty): 

1734 # used internally by joined eager loader...possibly not ideal 

1735 prop = onclause 

1736 on_selectable = prop.parent.selectable 

1737 else: 

1738 prop = None 

1739 

1740 if prop: 

1741 left_selectable = left_info.selectable 

1742 

1743 if sql_util.clause_is_present(on_selectable, left_selectable): 

1744 adapt_from = on_selectable 

1745 else: 

1746 adapt_from = left_selectable 

1747 

1748 ( 

1749 pj, 

1750 sj, 

1751 source, 

1752 dest, 

1753 secondary, 

1754 target_adapter, 

1755 ) = prop._create_joins( 

1756 source_selectable=adapt_from, 

1757 dest_selectable=adapt_to, 

1758 source_polymorphic=True, 

1759 of_type_entity=right_info, 

1760 alias_secondary=True, 

1761 extra_criteria=_extra_criteria, 

1762 ) 

1763 

1764 if sj is not None: 

1765 if isouter: 

1766 # note this is an inner join from secondary->right 

1767 right = sql.join(secondary, right, sj) 

1768 onclause = pj 

1769 else: 

1770 left = sql.join(left, secondary, pj, isouter) 

1771 onclause = sj 

1772 else: 

1773 onclause = pj 

1774 

1775 self._target_adapter = target_adapter 

1776 

1777 # we don't use the normal coercions logic for _ORMJoin 

1778 # (probably should), so do some gymnastics to get the entity. 

1779 # logic here is for #8721, which was a major bug in 1.4 

1780 # for almost two years, not reported/fixed until 1.4.43 (!) 

1781 if left_info.is_selectable: 

1782 parententity = left_selectable._annotations.get( 

1783 "parententity", None 

1784 ) 

1785 elif left_info.is_mapper or left_info.is_aliased_class: 

1786 parententity = left_info 

1787 else: 

1788 parententity = None 

1789 

1790 if parententity is not None: 

1791 self._annotations = self._annotations.union( 

1792 {"parententity": parententity} 

1793 ) 

1794 

1795 augment_onclause = onclause is None and _extra_criteria 

1796 expression.Join.__init__(self, left, right, onclause, isouter, full) 

1797 

1798 if augment_onclause: 

1799 self.onclause &= sql.and_(*_extra_criteria) 

1800 

1801 if ( 

1802 not prop 

1803 and getattr(right_info, "mapper", None) 

1804 and right_info.mapper.single 

1805 ): 

1806 # if single inheritance target and we are using a manual 

1807 # or implicit ON clause, augment it the same way we'd augment the 

1808 # WHERE. 

1809 single_crit = right_info.mapper._single_table_criterion 

1810 if single_crit is not None: 

1811 if right_info.is_aliased_class: 

1812 single_crit = right_info._adapter.traverse(single_crit) 

1813 self.onclause = self.onclause & single_crit 

1814 

1815 def _splice_into_center(self, other): 

1816 """Splice a join into the center. 

1817 

1818 Given join(a, b) and join(b, c), return join(a, b).join(c) 

1819 

1820 """ 

1821 leftmost = other 

1822 while isinstance(leftmost, sql.Join): 

1823 leftmost = leftmost.left 

1824 

1825 assert self.right is leftmost 

1826 

1827 left = _ORMJoin( 

1828 self.left, 

1829 other.left, 

1830 self.onclause, 

1831 isouter=self.isouter, 

1832 _left_memo=self._left_memo, 

1833 _right_memo=other._left_memo, 

1834 ) 

1835 

1836 return _ORMJoin( 

1837 left, 

1838 other.right, 

1839 other.onclause, 

1840 isouter=other.isouter, 

1841 _right_memo=other._right_memo, 

1842 ) 

1843 

1844 def join( 

1845 self, 

1846 right, 

1847 onclause=None, 

1848 isouter=False, 

1849 full=False, 

1850 join_to_left=None, 

1851 ): 

1852 return _ORMJoin(self, right, onclause, full=full, isouter=isouter) 

1853 

1854 def outerjoin(self, right, onclause=None, full=False, join_to_left=None): 

1855 return _ORMJoin(self, right, onclause, isouter=True, full=full) 

1856 

1857 

1858def join( 

1859 left, right, onclause=None, isouter=False, full=False, join_to_left=None 

1860): 

1861 r"""Produce an inner join between left and right clauses. 

1862 

1863 :func:`_orm.join` is an extension to the core join interface 

1864 provided by :func:`_expression.join()`, where the 

1865 left and right selectables may be not only core selectable 

1866 objects such as :class:`_schema.Table`, but also mapped classes or 

1867 :class:`.AliasedClass` instances. The "on" clause can 

1868 be a SQL expression or an ORM mapped attribute 

1869 referencing a configured :func:`_orm.relationship`. 

1870 

1871 .. deprecated:: 1.4 using a string relationship name for the "onclause" 

1872 is deprecated and will be removed in 2.0; the onclause may be only 

1873 an ORM-mapped relationship attribute or a SQL expression construct. 

1874 

1875 :func:`_orm.join` is not commonly needed in modern usage, 

1876 as its functionality is encapsulated within that of the 

1877 :meth:`_sql.Select.join` and :meth:`_query.Query.join` 

1878 methods. which feature a 

1879 significant amount of automation beyond :func:`_orm.join` 

1880 by itself. Explicit use of :func:`_orm.join` 

1881 with ORM-enabled SELECT statements involves use of the 

1882 :meth:`_sql.Select.select_from` method, as in:: 

1883 

1884 from sqlalchemy.orm import join 

1885 stmt = select(User).\ 

1886 select_from(join(User, Address, User.addresses)).\ 

1887 filter(Address.email_address=='foo@bar.com') 

1888 

1889 In modern SQLAlchemy the above join can be written more 

1890 succinctly as:: 

1891 

1892 stmt = select(User).\ 

1893 join(User.addresses).\ 

1894 filter(Address.email_address=='foo@bar.com') 

1895 

1896 .. warning:: using :func:`_orm.join` directly may not work properly 

1897 with modern ORM options such as :func:`_orm.with_loader_criteria`. 

1898 It is strongly recommended to use the idiomatic join patterns 

1899 provided by methods such as :meth:`.Select.join` and 

1900 :meth:`.Select.join_from` when creating ORM joins. 

1901 

1902 .. seealso:: 

1903 

1904 :ref:`orm_queryguide_joins` - in the :ref:`queryguide_toplevel` for 

1905 background on idiomatic ORM join patterns 

1906 

1907 """ 

1908 return _ORMJoin(left, right, onclause, isouter, full) 

1909 

1910 

1911def outerjoin(left, right, onclause=None, full=False, join_to_left=None): 

1912 """Produce a left outer join between left and right clauses. 

1913 

1914 This is the "outer join" version of the :func:`_orm.join` function, 

1915 featuring the same behavior except that an OUTER JOIN is generated. 

1916 See that function's documentation for other usage details. 

1917 

1918 """ 

1919 return _ORMJoin(left, right, onclause, True, full) 

1920 

1921 

1922def with_parent(instance, prop, from_entity=None): 

1923 """Create filtering criterion that relates this query's primary entity 

1924 to the given related instance, using established 

1925 :func:`_orm.relationship()` 

1926 configuration. 

1927 

1928 E.g.:: 

1929 

1930 stmt = select(Address).where(with_parent(some_user, User.addresses)) 

1931 

1932 

1933 The SQL rendered is the same as that rendered when a lazy loader 

1934 would fire off from the given parent on that attribute, meaning 

1935 that the appropriate state is taken from the parent object in 

1936 Python without the need to render joins to the parent table 

1937 in the rendered statement. 

1938 

1939 The given property may also make use of :meth:`_orm.PropComparator.of_type` 

1940 to indicate the left side of the criteria:: 

1941 

1942 

1943 a1 = aliased(Address) 

1944 a2 = aliased(Address) 

1945 stmt = select(a1, a2).where( 

1946 with_parent(u1, User.addresses.of_type(a2)) 

1947 ) 

1948 

1949 The above use is equivalent to using the 

1950 :func:`_orm.with_parent.from_entity` argument:: 

1951 

1952 a1 = aliased(Address) 

1953 a2 = aliased(Address) 

1954 stmt = select(a1, a2).where( 

1955 with_parent(u1, User.addresses, from_entity=a2) 

1956 ) 

1957 

1958 :param instance: 

1959 An instance which has some :func:`_orm.relationship`. 

1960 

1961 :param property: 

1962 String property name, or class-bound attribute, which indicates 

1963 what relationship from the instance should be used to reconcile the 

1964 parent/child relationship. 

1965 

1966 .. deprecated:: 1.4 Using strings is deprecated and will be removed 

1967 in SQLAlchemy 2.0. Please use the class-bound attribute directly. 

1968 

1969 :param from_entity: 

1970 Entity in which to consider as the left side. This defaults to the 

1971 "zero" entity of the :class:`_query.Query` itself. 

1972 

1973 .. versionadded:: 1.2 

1974 

1975 """ 

1976 if isinstance(prop, util.string_types): 

1977 util.warn_deprecated_20( 

1978 "Using strings to indicate relationship names in the ORM " 

1979 "with_parent() function is deprecated and will be removed " 

1980 "SQLAlchemy 2.0. Please use the class-bound attribute directly." 

1981 ) 

1982 mapper = object_mapper(instance) 

1983 prop = getattr(mapper.class_, prop).property 

1984 elif isinstance(prop, attributes.QueryableAttribute): 

1985 if prop._of_type: 

1986 from_entity = prop._of_type 

1987 prop = prop.property 

1988 

1989 return prop._with_parent(instance, from_entity=from_entity) 

1990 

1991 

1992def has_identity(object_): 

1993 """Return True if the given object has a database 

1994 identity. 

1995 

1996 This typically corresponds to the object being 

1997 in either the persistent or detached state. 

1998 

1999 .. seealso:: 

2000 

2001 :func:`.was_deleted` 

2002 

2003 """ 

2004 state = attributes.instance_state(object_) 

2005 return state.has_identity 

2006 

2007 

2008def was_deleted(object_): 

2009 """Return True if the given object was deleted 

2010 within a session flush. 

2011 

2012 This is regardless of whether or not the object is 

2013 persistent or detached. 

2014 

2015 .. seealso:: 

2016 

2017 :attr:`.InstanceState.was_deleted` 

2018 

2019 """ 

2020 

2021 state = attributes.instance_state(object_) 

2022 return state.was_deleted 

2023 

2024 

2025def _entity_corresponds_to(given, entity): 

2026 """determine if 'given' corresponds to 'entity', in terms 

2027 of an entity passed to Query that would match the same entity 

2028 being referred to elsewhere in the query. 

2029 

2030 """ 

2031 if entity.is_aliased_class: 

2032 if given.is_aliased_class: 

2033 if entity._base_alias() is given._base_alias(): 

2034 return True 

2035 return False 

2036 elif given.is_aliased_class: 

2037 if given._use_mapper_path: 

2038 return entity in given.with_polymorphic_mappers 

2039 else: 

2040 return entity is given 

2041 

2042 return entity.common_parent(given) 

2043 

2044 

2045def _entity_corresponds_to_use_path_impl(given, entity): 

2046 """determine if 'given' corresponds to 'entity', in terms 

2047 of a path of loader options where a mapped attribute is taken to 

2048 be a member of a parent entity. 

2049 

2050 e.g.:: 

2051 

2052 someoption(A).someoption(A.b) # -> fn(A, A) -> True 

2053 someoption(A).someoption(C.d) # -> fn(A, C) -> False 

2054 

2055 a1 = aliased(A) 

2056 someoption(a1).someoption(A.b) # -> fn(a1, A) -> False 

2057 someoption(a1).someoption(a1.b) # -> fn(a1, a1) -> True 

2058 

2059 wp = with_polymorphic(A, [A1, A2]) 

2060 someoption(wp).someoption(A1.foo) # -> fn(wp, A1) -> False 

2061 someoption(wp).someoption(wp.A1.foo) # -> fn(wp, wp.A1) -> True 

2062 

2063 

2064 """ 

2065 if given.is_aliased_class: 

2066 return ( 

2067 entity.is_aliased_class 

2068 and not entity._use_mapper_path 

2069 and (given is entity or given in entity._with_polymorphic_entities) 

2070 ) 

2071 elif not entity.is_aliased_class: 

2072 return given.common_parent(entity.mapper) 

2073 else: 

2074 return ( 

2075 entity._use_mapper_path 

2076 and given in entity.with_polymorphic_mappers 

2077 ) 

2078 

2079 

2080def _entity_isa(given, mapper): 

2081 """determine if 'given' "is a" mapper, in terms of the given 

2082 would load rows of type 'mapper'. 

2083 

2084 """ 

2085 if given.is_aliased_class: 

2086 return mapper in given.with_polymorphic_mappers or given.mapper.isa( 

2087 mapper 

2088 ) 

2089 elif given.with_polymorphic_mappers: 

2090 return mapper in given.with_polymorphic_mappers 

2091 else: 

2092 return given.isa(mapper) 

2093 

2094 

2095def randomize_unitofwork(): 

2096 """Use random-ordering sets within the unit of work in order 

2097 to detect unit of work sorting issues. 

2098 

2099 This is a utility function that can be used to help reproduce 

2100 inconsistent unit of work sorting issues. For example, 

2101 if two kinds of objects A and B are being inserted, and 

2102 B has a foreign key reference to A - the A must be inserted first. 

2103 However, if there is no relationship between A and B, the unit of work 

2104 won't know to perform this sorting, and an operation may or may not 

2105 fail, depending on how the ordering works out. Since Python sets 

2106 and dictionaries have non-deterministic ordering, such an issue may 

2107 occur on some runs and not on others, and in practice it tends to 

2108 have a great dependence on the state of the interpreter. This leads 

2109 to so-called "heisenbugs" where changing entirely irrelevant aspects 

2110 of the test program still cause the failure behavior to change. 

2111 

2112 By calling ``randomize_unitofwork()`` when a script first runs, the 

2113 ordering of a key series of sets within the unit of work implementation 

2114 are randomized, so that the script can be minimized down to the 

2115 fundamental mapping and operation that's failing, while still reproducing 

2116 the issue on at least some runs. 

2117 

2118 This utility is also available when running the test suite via the 

2119 ``--reversetop`` flag. 

2120 

2121 """ 

2122 from sqlalchemy.orm import unitofwork, session, mapper, dependency 

2123 from sqlalchemy.util import topological 

2124 from sqlalchemy.testing.util import RandomSet 

2125 

2126 topological.set = ( 

2127 unitofwork.set 

2128 ) = session.set = mapper.set = dependency.set = RandomSet 

2129 

2130 

2131def _getitem(iterable_query, item, allow_negative): 

2132 """calculate __getitem__ in terms of an iterable query object 

2133 that also has a slice() method. 

2134 

2135 """ 

2136 

2137 def _no_negative_indexes(): 

2138 if not allow_negative: 

2139 raise IndexError( 

2140 "negative indexes are not accepted by SQL " 

2141 "index / slice operators" 

2142 ) 

2143 else: 

2144 util.warn_deprecated_20( 

2145 "Support for negative indexes for SQL index / slice operators " 

2146 "will be " 

2147 "removed in 2.0; these operators fetch the complete result " 

2148 "and do not work efficiently." 

2149 ) 

2150 

2151 if isinstance(item, slice): 

2152 start, stop, step = util.decode_slice(item) 

2153 

2154 if ( 

2155 isinstance(stop, int) 

2156 and isinstance(start, int) 

2157 and stop - start <= 0 

2158 ): 

2159 return [] 

2160 

2161 elif (isinstance(start, int) and start < 0) or ( 

2162 isinstance(stop, int) and stop < 0 

2163 ): 

2164 _no_negative_indexes() 

2165 return list(iterable_query)[item] 

2166 

2167 res = iterable_query.slice(start, stop) 

2168 if step is not None: 

2169 return list(res)[None : None : item.step] 

2170 else: 

2171 return list(res) 

2172 else: 

2173 if item == -1: 

2174 _no_negative_indexes() 

2175 return list(iterable_query)[-1] 

2176 else: 

2177 return list(iterable_query[item : item + 1])[0]