Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# orm/util.py 

2# Copyright (C) 2005-2020 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 

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 MapperProperty # noqa 

27from .interfaces import PropComparator # noqa 

28from .path_registry import PathRegistry # noqa 

29from .. import event 

30from .. import exc as sa_exc 

31from .. import inspection 

32from .. import sql 

33from .. import util 

34from ..sql import expression 

35from ..sql import util as sql_util 

36 

37 

38all_cascades = frozenset( 

39 ( 

40 "delete", 

41 "delete-orphan", 

42 "all", 

43 "merge", 

44 "expunge", 

45 "save-update", 

46 "refresh-expire", 

47 "none", 

48 ) 

49) 

50 

51 

52class CascadeOptions(frozenset): 

53 """Keeps track of the options sent to relationship().cascade""" 

54 

55 _add_w_all_cascades = all_cascades.difference( 

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

57 ) 

58 _allowed_cascades = all_cascades 

59 

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

61 

62 __slots__ = ( 

63 "save_update", 

64 "delete", 

65 "refresh_expire", 

66 "merge", 

67 "expunge", 

68 "delete_orphan", 

69 ) 

70 

71 def __new__(cls, value_list): 

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

73 return cls.from_string(value_list) 

74 values = set(value_list) 

75 if values.difference(cls._allowed_cascades): 

76 raise sa_exc.ArgumentError( 

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

78 % ", ".join( 

79 [ 

80 repr(x) 

81 for x in sorted( 

82 values.difference(cls._allowed_cascades) 

83 ) 

84 ] 

85 ) 

86 ) 

87 

88 if "all" in values: 

89 values.update(cls._add_w_all_cascades) 

90 if "none" in values: 

91 values.clear() 

92 values.discard("all") 

93 

94 self = frozenset.__new__(CascadeOptions, values) 

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

96 self.delete = "delete" in values 

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

98 self.merge = "merge" in values 

99 self.expunge = "expunge" in values 

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

101 

102 if self.delete_orphan and not self.delete: 

103 util.warn( 

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

105 ) 

106 return self 

107 

108 def __repr__(self): 

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

110 

111 @classmethod 

112 def from_string(cls, arg): 

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

114 return cls(values) 

115 

116 

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

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

119 appended. 

120 """ 

121 

122 if not include_backrefs: 

123 

124 def detect_is_backref(state, initiator): 

125 impl = state.manager[key].impl 

126 return initiator.impl is not impl 

127 

128 if include_removes: 

129 

130 def append(state, value, initiator): 

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

132 include_backrefs or not detect_is_backref(state, initiator) 

133 ): 

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

135 else: 

136 return value 

137 

138 def bulk_set(state, values, initiator): 

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

140 obj = state.obj() 

141 values[:] = [ 

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

143 ] 

144 

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

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

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

148 else: 

149 return value 

150 

151 def remove(state, value, initiator): 

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

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

154 

155 else: 

156 

157 def append(state, value, initiator): 

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

159 include_backrefs or not detect_is_backref(state, initiator) 

160 ): 

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

162 else: 

163 return value 

164 

165 def bulk_set(state, values, initiator): 

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

167 obj = state.obj() 

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

169 

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

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

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

173 else: 

174 return value 

175 

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

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

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

179 if include_removes: 

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

181 

182 

183def polymorphic_union( 

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

185): 

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

187 

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

189 this is used. 

190 

191 :param table_map: mapping of polymorphic identities to 

192 :class:`_schema.Table` objects. 

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

194 derived from the query, producing the polymorphic identity for 

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

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

197 construct generated. 

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

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

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

201 can be set to False. 

202 

203 """ 

204 

205 colnames = util.OrderedSet() 

206 colnamemaps = {} 

207 types = {} 

208 for key in table_map: 

209 table = table_map[key] 

210 

211 # mysql doesn't like selecting from a select; 

212 # make it an alias of the select 

213 if isinstance(table, sql.Select): 

214 table = table.alias() 

215 table_map[key] = table 

216 

217 m = {} 

218 for c in table.c: 

219 colnames.add(c.key) 

220 m[c.key] = c 

221 types[c.key] = c.type 

222 colnamemaps[table] = m 

223 

224 def col(name, table): 

225 try: 

226 return colnamemaps[table][name] 

227 except KeyError: 

228 if cast_nulls: 

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

230 else: 

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

232 

233 result = [] 

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

235 if typecolname is not None: 

236 result.append( 

237 sql.select( 

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

239 + [ 

240 sql.literal_column( 

241 sql_util._quote_ddl_expr(type_) 

242 ).label(typecolname) 

243 ], 

244 from_obj=[table], 

245 ) 

246 ) 

247 else: 

248 result.append( 

249 sql.select( 

250 [col(name, table) for name in colnames], from_obj=[table] 

251 ) 

252 ) 

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

254 

255 

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

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

258 :attr:`.Session.identity_map` dictionary. 

259 

260 This function has several call styles: 

261 

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

263 

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

265 tuple as an argument. 

266 

267 E.g.:: 

268 

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

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

271 

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

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

274 :param identity_token: optional identity token 

275 

276 .. versionadded:: 1.2 added identity_token 

277 

278 

279 * ``identity_key(instance=instance)`` 

280 

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

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

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

284 values). 

285 

286 E.g.:: 

287 

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

289 >>> identity_key(instance=instance) 

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

291 

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

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

294 effect of performing a database check for the corresponding row 

295 if the object is expired. 

296 

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

298 

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

300 

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

302 database result row as a :class:`.RowProxy` object. 

303 

304 E.g.:: 

305 

306 >>> row = engine.execute("select * from table where a=1 and b=2").\ 

307first() 

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

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

310 

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

312 :param row: :class:`.RowProxy` row returned by a 

313 :class:`_engine.ResultProxy` 

314 (must be given as a keyword arg) 

315 :param identity_token: optional identity token 

316 

317 .. versionadded:: 1.2 added identity_token 

318 

319 """ 

320 if args: 

321 row = None 

322 largs = len(args) 

323 if largs == 1: 

324 class_ = args[0] 

325 try: 

326 row = kwargs.pop("row") 

327 except KeyError: 

328 ident = kwargs.pop("ident") 

329 elif largs in (2, 3): 

330 class_, ident = args 

331 else: 

332 raise sa_exc.ArgumentError( 

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

334 ) 

335 

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

337 if kwargs: 

338 raise sa_exc.ArgumentError( 

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

340 ) 

341 mapper = class_mapper(class_) 

342 if row is None: 

343 return mapper.identity_key_from_primary_key( 

344 util.to_list(ident), identity_token=identity_token 

345 ) 

346 else: 

347 return mapper.identity_key_from_row( 

348 row, identity_token=identity_token 

349 ) 

350 else: 

351 instance = kwargs.pop("instance") 

352 if kwargs: 

353 raise sa_exc.ArgumentError( 

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

355 ) 

356 mapper = object_mapper(instance) 

357 return mapper.identity_key_from_instance(instance) 

358 

359 

360class ORMAdapter(sql_util.ColumnAdapter): 

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

362 non-matching mappers. 

363 

364 """ 

365 

366 def __init__( 

367 self, 

368 entity, 

369 equivalents=None, 

370 adapt_required=False, 

371 allow_label_resolve=True, 

372 anonymize_labels=False, 

373 ): 

374 info = inspection.inspect(entity) 

375 

376 self.mapper = info.mapper 

377 selectable = info.selectable 

378 is_aliased_class = info.is_aliased_class 

379 if is_aliased_class: 

380 self.aliased_class = entity 

381 else: 

382 self.aliased_class = None 

383 

384 sql_util.ColumnAdapter.__init__( 

385 self, 

386 selectable, 

387 equivalents, 

388 adapt_required=adapt_required, 

389 allow_label_resolve=allow_label_resolve, 

390 anonymize_labels=anonymize_labels, 

391 include_fn=self._include_fn, 

392 ) 

393 

394 def _include_fn(self, elem): 

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

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

397 

398 

399class AliasedClass(object): 

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

401 

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

403 construct, this object mimics the mapped class using a 

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

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

406 

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

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

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

410 

411 # find all pairs of users with the same name 

412 user_alias = aliased(User) 

413 session.query(User, user_alias).\ 

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

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

416 

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

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

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

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

421 See the links below for examples. 

422 

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

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

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

426 

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

428 This object implements an attribute scheme which produces the 

429 same attribute and method interface as the original mapped 

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

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

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

433 

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

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

436 using :func:`_sa.inspect`:: 

437 

438 from sqlalchemy import inspect 

439 my_alias = aliased(MyClass) 

440 insp = inspect(my_alias) 

441 

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

443 

444 

445 .. seealso:: 

446 

447 :func:`.aliased` 

448 

449 :func:`.with_polymorphic` 

450 

451 :ref:`relationship_aliased_class` 

452 

453 :ref:`relationship_to_window_function` 

454 

455 

456 """ 

457 

458 def __init__( 

459 self, 

460 cls, 

461 alias=None, 

462 name=None, 

463 flat=False, 

464 adapt_on_names=False, 

465 # TODO: None for default here? 

466 with_polymorphic_mappers=(), 

467 with_polymorphic_discriminator=None, 

468 base_alias=None, 

469 use_mapper_path=False, 

470 represents_outer_join=False, 

471 ): 

472 mapper = _class_to_mapper(cls) 

473 if alias is None: 

474 alias = mapper._with_polymorphic_selectable.alias( 

475 name=name, flat=flat 

476 ) 

477 

478 self._aliased_insp = AliasedInsp( 

479 self, 

480 mapper, 

481 alias, 

482 name, 

483 with_polymorphic_mappers 

484 if with_polymorphic_mappers 

485 else mapper.with_polymorphic_mappers, 

486 with_polymorphic_discriminator 

487 if with_polymorphic_discriminator is not None 

488 else mapper.polymorphic_on, 

489 base_alias, 

490 use_mapper_path, 

491 adapt_on_names, 

492 represents_outer_join, 

493 ) 

494 

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

496 

497 def __getattr__(self, key): 

498 try: 

499 _aliased_insp = self.__dict__["_aliased_insp"] 

500 except KeyError: 

501 raise AttributeError() 

502 else: 

503 target = _aliased_insp._target 

504 # maintain all getattr mechanics 

505 attr = getattr(target, key) 

506 

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

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

509 # new self 

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

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

512 

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

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

515 if hasattr(attr, "__get__"): 

516 attr = attr.__get__(None, self) 

517 

518 # attributes within the QueryableAttribute system will want this 

519 # to be invoked so the object can be adapted 

520 if hasattr(attr, "adapt_to_entity"): 

521 attr = attr.adapt_to_entity(_aliased_insp) 

522 setattr(self, key, attr) 

523 

524 return attr 

525 

526 def __repr__(self): 

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

528 id(self), 

529 self._aliased_insp._target.__name__, 

530 ) 

531 

532 def __str__(self): 

533 return str(self._aliased_insp) 

534 

535 

536class AliasedInsp(InspectionAttr): 

537 """Provide an inspection interface for an 

538 :class:`.AliasedClass` object. 

539 

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

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

542 :func:`_sa.inspect` function:: 

543 

544 from sqlalchemy import inspect 

545 from sqlalchemy.orm import aliased 

546 

547 my_alias = aliased(MyMappedClass) 

548 insp = inspect(my_alias) 

549 

550 Attributes on :class:`.AliasedInsp` 

551 include: 

552 

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

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

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

556 construct which ultimately 

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

558 :class:`_expression.Select` 

559 construct. 

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

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

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

563 objects 

564 indicating all those mappers expressed in the select construct 

565 for the :class:`.AliasedClass`. 

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

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

568 

569 .. seealso:: 

570 

571 :ref:`inspection_toplevel` 

572 

573 """ 

574 

575 def __init__( 

576 self, 

577 entity, 

578 mapper, 

579 selectable, 

580 name, 

581 with_polymorphic_mappers, 

582 polymorphic_on, 

583 _base_alias, 

584 _use_mapper_path, 

585 adapt_on_names, 

586 represents_outer_join, 

587 ): 

588 self._weak_entity = weakref.ref(entity) 

589 self.mapper = mapper 

590 self.selectable = ( 

591 self.persist_selectable 

592 ) = self.local_table = selectable 

593 self.name = name 

594 self.polymorphic_on = polymorphic_on 

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

596 self._use_mapper_path = _use_mapper_path 

597 self.represents_outer_join = represents_outer_join 

598 

599 if with_polymorphic_mappers: 

600 self._is_with_polymorphic = True 

601 self.with_polymorphic_mappers = with_polymorphic_mappers 

602 self._with_polymorphic_entities = [] 

603 for poly in self.with_polymorphic_mappers: 

604 if poly is not mapper: 

605 ent = AliasedClass( 

606 poly.class_, 

607 selectable, 

608 base_alias=self, 

609 adapt_on_names=adapt_on_names, 

610 use_mapper_path=_use_mapper_path, 

611 ) 

612 

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

614 self._with_polymorphic_entities.append(ent._aliased_insp) 

615 

616 else: 

617 self._is_with_polymorphic = False 

618 self.with_polymorphic_mappers = [mapper] 

619 

620 self._adapter = sql_util.ColumnAdapter( 

621 selectable, 

622 equivalents=mapper._equivalent_columns, 

623 adapt_on_names=adapt_on_names, 

624 anonymize_labels=True, 

625 ) 

626 

627 self._adapt_on_names = adapt_on_names 

628 self._target = mapper.class_ 

629 

630 @property 

631 def entity(self): 

632 return self._weak_entity() 

633 

634 is_aliased_class = True 

635 "always returns True" 

636 

637 @property 

638 def class_(self): 

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

640 :class:`.AliasedInsp`.""" 

641 return self.mapper.class_ 

642 

643 @property 

644 def _path_registry(self): 

645 if self._use_mapper_path: 

646 return self.mapper._path_registry 

647 else: 

648 return PathRegistry.per_mapper(self) 

649 

650 def __getstate__(self): 

651 return { 

652 "entity": self.entity, 

653 "mapper": self.mapper, 

654 "alias": self.selectable, 

655 "name": self.name, 

656 "adapt_on_names": self._adapt_on_names, 

657 "with_polymorphic_mappers": self.with_polymorphic_mappers, 

658 "with_polymorphic_discriminator": self.polymorphic_on, 

659 "base_alias": self._base_alias(), 

660 "use_mapper_path": self._use_mapper_path, 

661 "represents_outer_join": self.represents_outer_join, 

662 } 

663 

664 def __setstate__(self, state): 

665 self.__init__( 

666 state["entity"], 

667 state["mapper"], 

668 state["alias"], 

669 state["name"], 

670 state["with_polymorphic_mappers"], 

671 state["with_polymorphic_discriminator"], 

672 state["base_alias"], 

673 state["use_mapper_path"], 

674 state["adapt_on_names"], 

675 state["represents_outer_join"], 

676 ) 

677 

678 def _adapt_element(self, elem): 

679 return self._adapter.traverse(elem)._annotate( 

680 {"parententity": self, "parentmapper": self.mapper} 

681 ) 

682 

683 def _entity_for_mapper(self, mapper): 

684 self_poly = self.with_polymorphic_mappers 

685 if mapper in self_poly: 

686 if mapper is self.mapper: 

687 return self 

688 else: 

689 return getattr( 

690 self.entity, mapper.class_.__name__ 

691 )._aliased_insp 

692 elif mapper.isa(self.mapper): 

693 return self 

694 else: 

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

696 

697 @util.memoized_property 

698 def _get_clause(self): 

699 onclause, replacemap = self.mapper._get_clause 

700 return ( 

701 self._adapter.traverse(onclause), 

702 { 

703 self._adapter.traverse(col): param 

704 for col, param in replacemap.items() 

705 }, 

706 ) 

707 

708 @util.memoized_property 

709 def _memoized_values(self): 

710 return {} 

711 

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

713 if key in self._memoized_values: 

714 return self._memoized_values[key] 

715 else: 

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

717 return value 

718 

719 def __repr__(self): 

720 if self.with_polymorphic_mappers: 

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

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

723 ) 

724 else: 

725 with_poly = "" 

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

727 id(self), 

728 self.class_.__name__, 

729 with_poly, 

730 ) 

731 

732 def __str__(self): 

733 if self._is_with_polymorphic: 

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

735 self._target.__name__, 

736 ", ".join( 

737 mp.class_.__name__ 

738 for mp in self.with_polymorphic_mappers 

739 if mp is not self.mapper 

740 ), 

741 ) 

742 else: 

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

744 

745 

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

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

748 

749 

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

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

752 instance. 

753 

754 E.g.:: 

755 

756 my_alias = aliased(MyClass) 

757 

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

759 

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

761 of a mapped class to a new selectable. By default, a selectable 

762 is generated from the normally mapped selectable (typically a 

763 :class:`_schema.Table`) using the :meth:`_expression.FromClause.alias` 

764 method. 

765 However, :func:`.aliased` can also be used to link the class to 

766 a new :func:`_expression.select` statement. Also, the 

767 :func:`.with_polymorphic` 

768 function is a variant of :func:`.aliased` that is intended to specify 

769 a so-called "polymorphic selectable", that corresponds to the union 

770 of several joined-inheritance subclasses at once. 

771 

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

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

774 :class:`_schema.Table` or 

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

776 :meth:`_expression.FromClause.alias` 

777 method is called on the object and the new :class:`_expression.Alias` 

778 object 

779 returned. The returned :class:`_expression.Alias` 

780 is not ORM-mapped in this case. 

781 

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

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

784 . 

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

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

787 select construct as one would produce from the 

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

789 the :meth:`_expression.Select.alias` methods of the 

790 :func:`_expression.select` construct. 

791 

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

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

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

795 :class:`_query.Query` object. 

796 

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

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

799 :class:`_expression.Join` objects 

800 don't include an enclosing SELECT. This can lead to more efficient 

801 queries in many circumstances. A JOIN against a nested JOIN will be 

802 rewritten as a JOIN against an aliased SELECT subquery on backends that 

803 don't support this syntax. 

804 

805 .. seealso:: :meth:`_expression.Join.alias` 

806 

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

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

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

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

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

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

813 aggregate functions:: 

814 

815 class UnitPrice(Base): 

816 __tablename__ = 'unit_price' 

817 ... 

818 unit_id = Column(Integer) 

819 price = Column(Numeric) 

820 

821 aggregated_unit_price = Session.query( 

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

823 ).group_by(UnitPrice.unit_id).subquery() 

824 

825 aggregated_unit_price = aliased(UnitPrice, 

826 alias=aggregated_unit_price, adapt_on_names=True) 

827 

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

829 ``.price`` will return the 

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

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

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

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

834 

835 """ 

836 if isinstance(element, expression.FromClause): 

837 if adapt_on_names: 

838 raise sa_exc.ArgumentError( 

839 "adapt_on_names only applies to ORM elements" 

840 ) 

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

842 else: 

843 return AliasedClass( 

844 element, 

845 alias=alias, 

846 flat=flat, 

847 name=name, 

848 adapt_on_names=adapt_on_names, 

849 ) 

850 

851 

852def with_polymorphic( 

853 base, 

854 classes, 

855 selectable=False, 

856 flat=False, 

857 polymorphic_on=None, 

858 aliased=False, 

859 innerjoin=False, 

860 _use_mapper_path=False, 

861 _existing_alias=None, 

862): 

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

864 columns for descendant mappers of the given base. 

865 

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

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

868 criterion to be used against those tables. The resulting 

869 instances will also have those columns already loaded so that 

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

871 

872 .. seealso:: 

873 

874 :ref:`with_polymorphic` - full discussion of 

875 :func:`_orm.with_polymorphic`. 

876 

877 :param base: Base class to be aliased. 

878 

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

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

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

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

883 

884 :param aliased: when True, the selectable will be wrapped in an 

885 alias, that is ``(SELECT * FROM <fromclauses>) AS anon_1``. 

886 This can be important when using the with_polymorphic() 

887 to create the target of a JOIN on a backend that does not 

888 support parenthesized joins, such as SQLite and older 

889 versions of MySQL. However if the 

890 :paramref:`.with_polymorphic.selectable` parameter is in use 

891 with an existing :class:`_expression.Alias` construct, 

892 then you should not 

893 set this flag. 

894 

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

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

897 :class:`_expression.Join` 

898 objects don't include an enclosing SELECT. This can lead to more 

899 efficient queries in many circumstances. A JOIN against a nested JOIN 

900 will be rewritten as a JOIN against an aliased SELECT subquery on 

901 backends that don't support this syntax. 

902 

903 Setting ``flat`` to ``True`` implies the ``aliased`` flag is 

904 also ``True``. 

905 

906 .. versionadded:: 0.9.0 

907 

908 .. seealso:: :meth:`_expression.Join.alias` 

909 

910 :param selectable: a table or select() statement that will 

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

912 required if any of the desired classes use concrete table 

913 inheritance, since SQLAlchemy currently cannot generate UNIONs 

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

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

916 mapped class. Otherwise, the unaccounted mapped columns will 

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

918 which will usually lead to incorrect results. 

919 

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

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

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

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

924 behavior by default. 

925 

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

927 only be specified if querying for one specific subtype only 

928 """ 

929 primary_mapper = _class_to_mapper(base) 

930 if _existing_alias: 

931 assert _existing_alias.mapper is primary_mapper 

932 classes = util.to_set(classes) 

933 new_classes = set( 

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

935 ) 

936 if classes == new_classes: 

937 return _existing_alias 

938 else: 

939 classes = classes.union(new_classes) 

940 mappers, selectable = primary_mapper._with_polymorphic_args( 

941 classes, selectable, innerjoin=innerjoin 

942 ) 

943 if aliased or flat: 

944 selectable = selectable.alias(flat=flat) 

945 return AliasedClass( 

946 base, 

947 selectable, 

948 with_polymorphic_mappers=mappers, 

949 with_polymorphic_discriminator=polymorphic_on, 

950 use_mapper_path=_use_mapper_path, 

951 represents_outer_join=not innerjoin, 

952 ) 

953 

954 

955def _orm_annotate(element, exclude=None): 

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

957 "_orm_adapt" flag. 

958 

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

960 

961 """ 

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

963 

964 

965def _orm_deannotate(element): 

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

967 

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

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

970 annotators. 

971 

972 """ 

973 

974 return sql_util._deep_deannotate( 

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

976 ) 

977 

978 

979def _orm_full_deannotate(element): 

980 return sql_util._deep_deannotate(element) 

981 

982 

983class _ORMJoin(expression.Join): 

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

985 

986 __visit_name__ = expression.Join.__visit_name__ 

987 

988 def __init__( 

989 self, 

990 left, 

991 right, 

992 onclause=None, 

993 isouter=False, 

994 full=False, 

995 _left_memo=None, 

996 _right_memo=None, 

997 ): 

998 left_info = inspection.inspect(left) 

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

1000 

1001 right_info = inspection.inspect(right) 

1002 adapt_to = right_info.selectable 

1003 

1004 self._joined_from_info = right_info 

1005 

1006 self._left_memo = _left_memo 

1007 self._right_memo = _right_memo 

1008 

1009 if isinstance(onclause, util.string_types): 

1010 onclause = getattr(left_orm_info.entity, onclause) 

1011 

1012 if isinstance(onclause, attributes.QueryableAttribute): 

1013 on_selectable = onclause.comparator._source_selectable() 

1014 prop = onclause.property 

1015 elif isinstance(onclause, MapperProperty): 

1016 prop = onclause 

1017 on_selectable = prop.parent.selectable 

1018 else: 

1019 prop = None 

1020 

1021 if prop: 

1022 if sql_util.clause_is_present(on_selectable, left_info.selectable): 

1023 adapt_from = on_selectable 

1024 else: 

1025 adapt_from = left_info.selectable 

1026 

1027 ( 

1028 pj, 

1029 sj, 

1030 source, 

1031 dest, 

1032 secondary, 

1033 target_adapter, 

1034 ) = prop._create_joins( 

1035 source_selectable=adapt_from, 

1036 dest_selectable=adapt_to, 

1037 source_polymorphic=True, 

1038 dest_polymorphic=True, 

1039 of_type_mapper=right_info.mapper, 

1040 alias_secondary=True, 

1041 ) 

1042 

1043 if sj is not None: 

1044 if isouter: 

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

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

1047 onclause = pj 

1048 else: 

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

1050 onclause = sj 

1051 else: 

1052 onclause = pj 

1053 self._target_adapter = target_adapter 

1054 

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

1056 

1057 if ( 

1058 not prop 

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

1060 and right_info.mapper.single 

1061 ): 

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

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

1064 # WHERE. 

1065 single_crit = right_info.mapper._single_table_criterion 

1066 if single_crit is not None: 

1067 if right_info.is_aliased_class: 

1068 single_crit = right_info._adapter.traverse(single_crit) 

1069 self.onclause = self.onclause & single_crit 

1070 

1071 def _splice_into_center(self, other): 

1072 """Splice a join into the center. 

1073 

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

1075 

1076 """ 

1077 leftmost = other 

1078 while isinstance(leftmost, sql.Join): 

1079 leftmost = leftmost.left 

1080 

1081 assert self.right is leftmost 

1082 

1083 left = _ORMJoin( 

1084 self.left, 

1085 other.left, 

1086 self.onclause, 

1087 isouter=self.isouter, 

1088 _left_memo=self._left_memo, 

1089 _right_memo=other._left_memo, 

1090 ) 

1091 

1092 return _ORMJoin( 

1093 left, 

1094 other.right, 

1095 other.onclause, 

1096 isouter=other.isouter, 

1097 _right_memo=other._right_memo, 

1098 ) 

1099 

1100 def join( 

1101 self, 

1102 right, 

1103 onclause=None, 

1104 isouter=False, 

1105 full=False, 

1106 join_to_left=None, 

1107 ): 

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

1109 

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

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

1112 

1113 

1114def join( 

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

1116): 

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

1118 

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

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

1121 left and right selectables may be not only core selectable 

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

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

1124 be a SQL expression, or an attribute or string name 

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

1126 

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

1128 as its functionality is encapsulated within that of the 

1129 :meth:`_query.Query.join` method, which features a 

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

1131 by itself. Explicit usage of :func:`_orm.join` 

1132 with :class:`_query.Query` involves usage of the 

1133 :meth:`_query.Query.select_from` method, as in:: 

1134 

1135 from sqlalchemy.orm import join 

1136 session.query(User).\ 

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

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

1139 

1140 In modern SQLAlchemy the above join can be written more 

1141 succinctly as:: 

1142 

1143 session.query(User).\ 

1144 join(User.addresses).\ 

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

1146 

1147 See :meth:`_query.Query.join` for information on modern usage 

1148 of ORM level joins. 

1149 

1150 .. deprecated:: 0.8 

1151 

1152 the ``join_to_left`` parameter is deprecated, and will be removed 

1153 in a future release. The parameter has no effect. 

1154 

1155 """ 

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

1157 

1158 

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

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

1161 

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

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

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

1165 

1166 """ 

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

1168 

1169 

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

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

1172 to the given related instance, using established 

1173 :func:`_orm.relationship()` 

1174 configuration. 

1175 

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

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

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

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

1180 in the rendered statement. 

1181 

1182 :param instance: 

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

1184 

1185 :param property: 

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

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

1188 parent/child relationship. 

1189 

1190 :param from_entity: 

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

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

1193 

1194 .. versionadded:: 1.2 

1195 

1196 """ 

1197 if isinstance(prop, util.string_types): 

1198 mapper = object_mapper(instance) 

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

1200 elif isinstance(prop, attributes.QueryableAttribute): 

1201 prop = prop.property 

1202 

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

1204 

1205 

1206def has_identity(object_): 

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

1208 identity. 

1209 

1210 This typically corresponds to the object being 

1211 in either the persistent or detached state. 

1212 

1213 .. seealso:: 

1214 

1215 :func:`.was_deleted` 

1216 

1217 """ 

1218 state = attributes.instance_state(object_) 

1219 return state.has_identity 

1220 

1221 

1222def was_deleted(object_): 

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

1224 within a session flush. 

1225 

1226 This is regardless of whether or not the object is 

1227 persistent or detached. 

1228 

1229 .. seealso:: 

1230 

1231 :attr:`.InstanceState.was_deleted` 

1232 

1233 """ 

1234 

1235 state = attributes.instance_state(object_) 

1236 return state.was_deleted 

1237 

1238 

1239def _entity_corresponds_to(given, entity): 

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

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

1242 being referred to elsewhere in the query. 

1243 

1244 """ 

1245 if entity.is_aliased_class: 

1246 if given.is_aliased_class: 

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

1248 return True 

1249 return False 

1250 elif given.is_aliased_class: 

1251 if given._use_mapper_path: 

1252 return entity in given.with_polymorphic_mappers 

1253 else: 

1254 return entity is given 

1255 

1256 return entity.common_parent(given) 

1257 

1258 

1259def _entity_corresponds_to_use_path_impl(given, entity): 

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

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

1262 be a member of a parent entity. 

1263 

1264 e.g.:: 

1265 

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

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

1268 

1269 a1 = aliased(A) 

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

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

1272 

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

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

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

1276 

1277 

1278 """ 

1279 if given.is_aliased_class: 

1280 return ( 

1281 entity.is_aliased_class 

1282 and not entity._use_mapper_path 

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

1284 ) 

1285 elif not entity.is_aliased_class: 

1286 return given.common_parent(entity.mapper) 

1287 else: 

1288 return ( 

1289 entity._use_mapper_path 

1290 and given in entity.with_polymorphic_mappers 

1291 ) 

1292 

1293 

1294def _entity_isa(given, mapper): 

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

1296 would load rows of type 'mapper'. 

1297 

1298 """ 

1299 if given.is_aliased_class: 

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

1301 mapper 

1302 ) 

1303 elif given.with_polymorphic_mappers: 

1304 return mapper in given.with_polymorphic_mappers 

1305 else: 

1306 return given.isa(mapper) 

1307 

1308 

1309def randomize_unitofwork(): 

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

1311 to detect unit of work sorting issues. 

1312 

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

1314 inconsistent unit of work sorting issues. For example, 

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

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

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

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

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

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

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

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

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

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

1325 

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

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

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

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

1330 the issue on at least some runs. 

1331 

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

1333 ``--reversetop`` flag. 

1334 

1335 """ 

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

1337 from sqlalchemy.util import topological 

1338 from sqlalchemy.testing.util import RandomSet 

1339 

1340 topological.set = ( 

1341 unitofwork.set 

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