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/interfaces.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""" 

9 

10Contains various base classes used throughout the ORM. 

11 

12Defines some key base classes prominent within the internals, 

13as well as the now-deprecated ORM extension classes. 

14 

15Other than the deprecated extensions, this module and the 

16classes within are mostly private, though some attributes 

17are exposed when inspecting mappings. 

18 

19""" 

20 

21from __future__ import absolute_import 

22 

23import collections 

24 

25from . import exc as orm_exc 

26from . import path_registry 

27from .base import _MappedAttribute # noqa 

28from .base import EXT_CONTINUE 

29from .base import EXT_SKIP 

30from .base import EXT_STOP 

31from .base import InspectionAttr # noqa 

32from .base import InspectionAttrInfo # noqa 

33from .base import MANYTOMANY 

34from .base import MANYTOONE 

35from .base import NOT_EXTENSION 

36from .base import ONETOMANY 

37from .. import inspect 

38from .. import util 

39from ..sql import operators 

40 

41 

42# imported later 

43MapperExtension = SessionExtension = AttributeExtension = None 

44 

45__all__ = ( 

46 "AttributeExtension", 

47 "EXT_CONTINUE", 

48 "EXT_STOP", 

49 "EXT_SKIP", 

50 "ONETOMANY", 

51 "MANYTOMANY", 

52 "MANYTOONE", 

53 "NOT_EXTENSION", 

54 "LoaderStrategy", 

55 "MapperExtension", 

56 "MapperOption", 

57 "MapperProperty", 

58 "PropComparator", 

59 "SessionExtension", 

60 "StrategizedProperty", 

61) 

62 

63 

64class MapperProperty(_MappedAttribute, InspectionAttr, util.MemoizedSlots): 

65 """Represent a particular class attribute mapped by :class:`_orm.Mapper`. 

66 

67 The most common occurrences of :class:`.MapperProperty` are the 

68 mapped :class:`_schema.Column`, which is represented in a mapping as 

69 an instance of :class:`.ColumnProperty`, 

70 and a reference to another class produced by :func:`_orm.relationship`, 

71 represented in the mapping as an instance of 

72 :class:`.RelationshipProperty`. 

73 

74 """ 

75 

76 __slots__ = ( 

77 "_configure_started", 

78 "_configure_finished", 

79 "parent", 

80 "key", 

81 "info", 

82 ) 

83 

84 cascade = frozenset() 

85 """The set of 'cascade' attribute names. 

86 

87 This collection is checked before the 'cascade_iterator' method is called. 

88 

89 The collection typically only applies to a RelationshipProperty. 

90 

91 """ 

92 

93 is_property = True 

94 """Part of the InspectionAttr interface; states this object is a 

95 mapper property. 

96 

97 """ 

98 

99 def _memoized_attr_info(self): 

100 """Info dictionary associated with the object, allowing user-defined 

101 data to be associated with this :class:`.InspectionAttr`. 

102 

103 The dictionary is generated when first accessed. Alternatively, 

104 it can be specified as a constructor argument to the 

105 :func:`.column_property`, :func:`_orm.relationship`, or 

106 :func:`.composite` 

107 functions. 

108 

109 .. versionchanged:: 1.0.0 :attr:`.MapperProperty.info` is also 

110 available on extension types via the 

111 :attr:`.InspectionAttrInfo.info` attribute, so that it can apply 

112 to a wider variety of ORM and extension constructs. 

113 

114 .. seealso:: 

115 

116 :attr:`.QueryableAttribute.info` 

117 

118 :attr:`.SchemaItem.info` 

119 

120 """ 

121 return {} 

122 

123 def setup(self, context, query_entity, path, adapter, **kwargs): 

124 """Called by Query for the purposes of constructing a SQL statement. 

125 

126 Each MapperProperty associated with the target mapper processes the 

127 statement referenced by the query context, adding columns and/or 

128 criterion as appropriate. 

129 

130 """ 

131 

132 def create_row_processor( 

133 self, context, path, mapper, result, adapter, populators 

134 ): 

135 """Produce row processing functions and append to the given 

136 set of populators lists. 

137 

138 """ 

139 

140 def cascade_iterator( 

141 self, type_, state, visited_instances=None, halt_on=None 

142 ): 

143 """Iterate through instances related to the given instance for 

144 a particular 'cascade', starting with this MapperProperty. 

145 

146 Return an iterator3-tuples (instance, mapper, state). 

147 

148 Note that the 'cascade' collection on this MapperProperty is 

149 checked first for the given type before cascade_iterator is called. 

150 

151 This method typically only applies to RelationshipProperty. 

152 

153 """ 

154 

155 return iter(()) 

156 

157 def set_parent(self, parent, init): 

158 """Set the parent mapper that references this MapperProperty. 

159 

160 This method is overridden by some subclasses to perform extra 

161 setup when the mapper is first known. 

162 

163 """ 

164 self.parent = parent 

165 

166 def instrument_class(self, mapper): 

167 """Hook called by the Mapper to the property to initiate 

168 instrumentation of the class attribute managed by this 

169 MapperProperty. 

170 

171 The MapperProperty here will typically call out to the 

172 attributes module to set up an InstrumentedAttribute. 

173 

174 This step is the first of two steps to set up an InstrumentedAttribute, 

175 and is called early in the mapper setup process. 

176 

177 The second step is typically the init_class_attribute step, 

178 called from StrategizedProperty via the post_instrument_class() 

179 hook. This step assigns additional state to the InstrumentedAttribute 

180 (specifically the "impl") which has been determined after the 

181 MapperProperty has determined what kind of persistence 

182 management it needs to do (e.g. scalar, object, collection, etc). 

183 

184 """ 

185 

186 def __init__(self): 

187 self._configure_started = False 

188 self._configure_finished = False 

189 

190 def init(self): 

191 """Called after all mappers are created to assemble 

192 relationships between mappers and perform other post-mapper-creation 

193 initialization steps. 

194 

195 """ 

196 self._configure_started = True 

197 self.do_init() 

198 self._configure_finished = True 

199 

200 @property 

201 def class_attribute(self): 

202 """Return the class-bound descriptor corresponding to this 

203 :class:`.MapperProperty`. 

204 

205 This is basically a ``getattr()`` call:: 

206 

207 return getattr(self.parent.class_, self.key) 

208 

209 I.e. if this :class:`.MapperProperty` were named ``addresses``, 

210 and the class to which it is mapped is ``User``, this sequence 

211 is possible:: 

212 

213 >>> from sqlalchemy import inspect 

214 >>> mapper = inspect(User) 

215 >>> addresses_property = mapper.attrs.addresses 

216 >>> addresses_property.class_attribute is User.addresses 

217 True 

218 >>> User.addresses.property is addresses_property 

219 True 

220 

221 

222 """ 

223 

224 return getattr(self.parent.class_, self.key) 

225 

226 def do_init(self): 

227 """Perform subclass-specific initialization post-mapper-creation 

228 steps. 

229 

230 This is a template method called by the ``MapperProperty`` 

231 object's init() method. 

232 

233 """ 

234 

235 def post_instrument_class(self, mapper): 

236 """Perform instrumentation adjustments that need to occur 

237 after init() has completed. 

238 

239 The given Mapper is the Mapper invoking the operation, which 

240 may not be the same Mapper as self.parent in an inheritance 

241 scenario; however, Mapper will always at least be a sub-mapper of 

242 self.parent. 

243 

244 This method is typically used by StrategizedProperty, which delegates 

245 it to LoaderStrategy.init_class_attribute() to perform final setup 

246 on the class-bound InstrumentedAttribute. 

247 

248 """ 

249 

250 def merge( 

251 self, 

252 session, 

253 source_state, 

254 source_dict, 

255 dest_state, 

256 dest_dict, 

257 load, 

258 _recursive, 

259 _resolve_conflict_map, 

260 ): 

261 """Merge the attribute represented by this ``MapperProperty`` 

262 from source to destination object. 

263 

264 """ 

265 

266 def __repr__(self): 

267 return "<%s at 0x%x; %s>" % ( 

268 self.__class__.__name__, 

269 id(self), 

270 getattr(self, "key", "no key"), 

271 ) 

272 

273 

274class PropComparator(operators.ColumnOperators): 

275 r"""Defines SQL operators for :class:`.MapperProperty` objects. 

276 

277 SQLAlchemy allows for operators to 

278 be redefined at both the Core and ORM level. :class:`.PropComparator` 

279 is the base class of operator redefinition for ORM-level operations, 

280 including those of :class:`.ColumnProperty`, 

281 :class:`.RelationshipProperty`, and :class:`.CompositeProperty`. 

282 

283 .. note:: With the advent of Hybrid properties introduced in SQLAlchemy 

284 0.7, as well as Core-level operator redefinition in 

285 SQLAlchemy 0.8, the use case for user-defined :class:`.PropComparator` 

286 instances is extremely rare. See :ref:`hybrids_toplevel` as well 

287 as :ref:`types_operators`. 

288 

289 User-defined subclasses of :class:`.PropComparator` may be created. The 

290 built-in Python comparison and math operator methods, such as 

291 :meth:`.operators.ColumnOperators.__eq__`, 

292 :meth:`.operators.ColumnOperators.__lt__`, and 

293 :meth:`.operators.ColumnOperators.__add__`, can be overridden to provide 

294 new operator behavior. The custom :class:`.PropComparator` is passed to 

295 the :class:`.MapperProperty` instance via the ``comparator_factory`` 

296 argument. In each case, 

297 the appropriate subclass of :class:`.PropComparator` should be used:: 

298 

299 # definition of custom PropComparator subclasses 

300 

301 from sqlalchemy.orm.properties import \ 

302 ColumnProperty,\ 

303 CompositeProperty,\ 

304 RelationshipProperty 

305 

306 class MyColumnComparator(ColumnProperty.Comparator): 

307 def __eq__(self, other): 

308 return self.__clause_element__() == other 

309 

310 class MyRelationshipComparator(RelationshipProperty.Comparator): 

311 def any(self, expression): 

312 "define the 'any' operation" 

313 # ... 

314 

315 class MyCompositeComparator(CompositeProperty.Comparator): 

316 def __gt__(self, other): 

317 "redefine the 'greater than' operation" 

318 

319 return sql.and_(*[a>b for a, b in 

320 zip(self.__clause_element__().clauses, 

321 other.__composite_values__())]) 

322 

323 

324 # application of custom PropComparator subclasses 

325 

326 from sqlalchemy.orm import column_property, relationship, composite 

327 from sqlalchemy import Column, String 

328 

329 class SomeMappedClass(Base): 

330 some_column = column_property(Column("some_column", String), 

331 comparator_factory=MyColumnComparator) 

332 

333 some_relationship = relationship(SomeOtherClass, 

334 comparator_factory=MyRelationshipComparator) 

335 

336 some_composite = composite( 

337 Column("a", String), Column("b", String), 

338 comparator_factory=MyCompositeComparator 

339 ) 

340 

341 Note that for column-level operator redefinition, it's usually 

342 simpler to define the operators at the Core level, using the 

343 :attr:`.TypeEngine.comparator_factory` attribute. See 

344 :ref:`types_operators` for more detail. 

345 

346 .. seealso:: 

347 

348 :class:`.ColumnProperty.Comparator` 

349 

350 :class:`.RelationshipProperty.Comparator` 

351 

352 :class:`.CompositeProperty.Comparator` 

353 

354 :class:`.ColumnOperators` 

355 

356 :ref:`types_operators` 

357 

358 :attr:`.TypeEngine.comparator_factory` 

359 

360 """ 

361 

362 __slots__ = "prop", "property", "_parententity", "_adapt_to_entity" 

363 

364 def __init__(self, prop, parentmapper, adapt_to_entity=None): 

365 self.prop = self.property = prop 

366 self._parententity = adapt_to_entity or parentmapper 

367 self._adapt_to_entity = adapt_to_entity 

368 

369 def __clause_element__(self): 

370 raise NotImplementedError("%r" % self) 

371 

372 def _query_clause_element(self): 

373 return self.__clause_element__() 

374 

375 def _bulk_update_tuples(self, value): 

376 return [(self.__clause_element__(), value)] 

377 

378 def adapt_to_entity(self, adapt_to_entity): 

379 """Return a copy of this PropComparator which will use the given 

380 :class:`.AliasedInsp` to produce corresponding expressions. 

381 """ 

382 return self.__class__(self.prop, self._parententity, adapt_to_entity) 

383 

384 @property 

385 def _parentmapper(self): 

386 """legacy; this is renamed to _parententity to be 

387 compatible with QueryableAttribute.""" 

388 return inspect(self._parententity).mapper 

389 

390 @property 

391 def adapter(self): 

392 """Produce a callable that adapts column expressions 

393 to suit an aliased version of this comparator. 

394 

395 """ 

396 if self._adapt_to_entity is None: 

397 return None 

398 else: 

399 return self._adapt_to_entity._adapt_element 

400 

401 @property 

402 def info(self): 

403 return self.property.info 

404 

405 @staticmethod 

406 def any_op(a, b, **kwargs): 

407 return a.any(b, **kwargs) 

408 

409 @staticmethod 

410 def has_op(a, b, **kwargs): 

411 return a.has(b, **kwargs) 

412 

413 @staticmethod 

414 def of_type_op(a, class_): 

415 return a.of_type(class_) 

416 

417 def of_type(self, class_): 

418 r"""Redefine this object in terms of a polymorphic subclass, 

419 :func:`.with_polymorphic` construct, or :func:`.aliased` construct. 

420 

421 Returns a new PropComparator from which further criterion can be 

422 evaluated. 

423 

424 e.g.:: 

425 

426 query.join(Company.employees.of_type(Engineer)).\ 

427 filter(Engineer.name=='foo') 

428 

429 :param \class_: a class or mapper indicating that criterion will be 

430 against this specific subclass. 

431 

432 .. seealso:: 

433 

434 :ref:`inheritance_of_type` 

435 

436 """ 

437 

438 return self.operate(PropComparator.of_type_op, class_) 

439 

440 def any(self, criterion=None, **kwargs): 

441 r"""Return true if this collection contains any member that meets the 

442 given criterion. 

443 

444 The usual implementation of ``any()`` is 

445 :meth:`.RelationshipProperty.Comparator.any`. 

446 

447 :param criterion: an optional ClauseElement formulated against the 

448 member class' table or attributes. 

449 

450 :param \**kwargs: key/value pairs corresponding to member class 

451 attribute names which will be compared via equality to the 

452 corresponding values. 

453 

454 """ 

455 

456 return self.operate(PropComparator.any_op, criterion, **kwargs) 

457 

458 def has(self, criterion=None, **kwargs): 

459 r"""Return true if this element references a member which meets the 

460 given criterion. 

461 

462 The usual implementation of ``has()`` is 

463 :meth:`.RelationshipProperty.Comparator.has`. 

464 

465 :param criterion: an optional ClauseElement formulated against the 

466 member class' table or attributes. 

467 

468 :param \**kwargs: key/value pairs corresponding to member class 

469 attribute names which will be compared via equality to the 

470 corresponding values. 

471 

472 """ 

473 

474 return self.operate(PropComparator.has_op, criterion, **kwargs) 

475 

476 

477class StrategizedProperty(MapperProperty): 

478 """A MapperProperty which uses selectable strategies to affect 

479 loading behavior. 

480 

481 There is a single strategy selected by default. Alternate 

482 strategies can be selected at Query time through the usage of 

483 ``StrategizedOption`` objects via the Query.options() method. 

484 

485 The mechanics of StrategizedProperty are used for every Query 

486 invocation for every mapped attribute participating in that Query, 

487 to determine first how the attribute will be rendered in SQL 

488 and secondly how the attribute will retrieve a value from a result 

489 row and apply it to a mapped object. The routines here are very 

490 performance-critical. 

491 

492 """ 

493 

494 __slots__ = ( 

495 "_strategies", 

496 "strategy", 

497 "_wildcard_token", 

498 "_default_path_loader_key", 

499 ) 

500 

501 strategy_wildcard_key = None 

502 

503 def _memoized_attr__wildcard_token(self): 

504 return ( 

505 "%s:%s" 

506 % (self.strategy_wildcard_key, path_registry._WILDCARD_TOKEN), 

507 ) 

508 

509 def _memoized_attr__default_path_loader_key(self): 

510 return ( 

511 "loader", 

512 ( 

513 "%s:%s" 

514 % (self.strategy_wildcard_key, path_registry._DEFAULT_TOKEN), 

515 ), 

516 ) 

517 

518 def _get_context_loader(self, context, path): 

519 load = None 

520 

521 search_path = path[self] 

522 

523 # search among: exact match, "attr.*", "default" strategy 

524 # if any. 

525 for path_key in ( 

526 search_path._loader_key, 

527 search_path._wildcard_path_loader_key, 

528 search_path._default_path_loader_key, 

529 ): 

530 if path_key in context.attributes: 

531 load = context.attributes[path_key] 

532 break 

533 

534 return load 

535 

536 def _get_strategy(self, key): 

537 try: 

538 return self._strategies[key] 

539 except KeyError: 

540 pass 

541 

542 # run outside to prevent transfer of exception context 

543 cls = self._strategy_lookup(self, *key) 

544 self._strategies[key] = self._strategies[cls] = strategy = cls( 

545 self, key 

546 ) 

547 return strategy 

548 

549 def setup(self, context, query_entity, path, adapter, **kwargs): 

550 loader = self._get_context_loader(context, path) 

551 if loader and loader.strategy: 

552 strat = self._get_strategy(loader.strategy) 

553 else: 

554 strat = self.strategy 

555 strat.setup_query( 

556 context, query_entity, path, loader, adapter, **kwargs 

557 ) 

558 

559 def create_row_processor( 

560 self, context, path, mapper, result, adapter, populators 

561 ): 

562 loader = self._get_context_loader(context, path) 

563 if loader and loader.strategy: 

564 strat = self._get_strategy(loader.strategy) 

565 else: 

566 strat = self.strategy 

567 strat.create_row_processor( 

568 context, path, loader, mapper, result, adapter, populators 

569 ) 

570 

571 def do_init(self): 

572 self._strategies = {} 

573 self.strategy = self._get_strategy(self.strategy_key) 

574 

575 def post_instrument_class(self, mapper): 

576 if ( 

577 not self.parent.non_primary 

578 and not mapper.class_manager._attr_has_impl(self.key) 

579 ): 

580 self.strategy.init_class_attribute(mapper) 

581 

582 _all_strategies = collections.defaultdict(dict) 

583 

584 @classmethod 

585 def strategy_for(cls, **kw): 

586 def decorate(dec_cls): 

587 # ensure each subclass of the strategy has its 

588 # own _strategy_keys collection 

589 if "_strategy_keys" not in dec_cls.__dict__: 

590 dec_cls._strategy_keys = [] 

591 key = tuple(sorted(kw.items())) 

592 cls._all_strategies[cls][key] = dec_cls 

593 dec_cls._strategy_keys.append(key) 

594 return dec_cls 

595 

596 return decorate 

597 

598 @classmethod 

599 def _strategy_lookup(cls, requesting_property, *key): 

600 for prop_cls in cls.__mro__: 

601 if prop_cls in cls._all_strategies: 

602 strategies = cls._all_strategies[prop_cls] 

603 try: 

604 return strategies[key] 

605 except KeyError: 

606 pass 

607 

608 for property_type, strats in cls._all_strategies.items(): 

609 if key in strats: 

610 intended_property_type = property_type 

611 actual_strategy = strats[key] 

612 break 

613 else: 

614 intended_property_type = None 

615 actual_strategy = None 

616 

617 raise orm_exc.LoaderStrategyException( 

618 cls, 

619 requesting_property, 

620 intended_property_type, 

621 actual_strategy, 

622 key, 

623 ) 

624 

625 

626class MapperOption(object): 

627 """Describe a modification to a Query.""" 

628 

629 propagate_to_loaders = False 

630 """if True, indicate this option should be carried along 

631 to "secondary" Query objects produced during lazy loads 

632 or refresh operations. 

633 

634 """ 

635 

636 def process_query(self, query): 

637 """Apply a modification to the given :class:`_query.Query`.""" 

638 

639 def process_query_conditionally(self, query): 

640 """same as process_query(), except that this option may not 

641 apply to the given query. 

642 

643 This is typically used during a lazy load or scalar refresh 

644 operation to propagate options stated in the original Query to the 

645 new Query being used for the load. It occurs for those options that 

646 specify propagate_to_loaders=True. 

647 

648 """ 

649 

650 self.process_query(query) 

651 

652 def _generate_cache_key(self, path): 

653 """Used by the "baked lazy loader" to see if this option can be cached. 

654 

655 The "baked lazy loader" refers to the :class:`_query.Query` that is 

656 produced during a lazy load operation for a mapped relationship. 

657 It does not yet apply to the "lazy" load operation for deferred 

658 or expired column attributes, however this may change in the future. 

659 

660 This loader generates SQL for a query only once and attempts to cache 

661 it; from that point on, if the SQL has been cached it will no longer 

662 run the :meth:`_query.Query.options` method of the 

663 :class:`_query.Query`. The 

664 :class:`.MapperOption` object that wishes to participate within a lazy 

665 load operation therefore needs to tell the baked loader that it either 

666 needs to forego this caching, or that it needs to include the state of 

667 the :class:`.MapperOption` itself as part of its cache key, otherwise 

668 SQL or other query state that has been affected by the 

669 :class:`.MapperOption` may be cached in place of a query that does not 

670 include these modifications, or the option may not be invoked at all. 

671 

672 By default, this method returns the value ``False``, which means 

673 the :class:`.BakedQuery` generated by the lazy loader will 

674 not cache the SQL when this :class:`.MapperOption` is present. 

675 This is the safest option and ensures both that the option is 

676 invoked every time, and also that the cache isn't filled up with 

677 an unlimited number of :class:`_query.Query` objects for an unlimited 

678 number of :class:`.MapperOption` objects. 

679 

680 .. versionchanged:: 1.2.8 the default return value of 

681 :meth:`.MapperOption._generate_cache_key` is False; previously it 

682 was ``None`` indicating "safe to cache, don't include as part of 

683 the cache key" 

684 

685 To enable caching of :class:`_query.Query` objects within lazy loaders 

686 , a 

687 given :class:`.MapperOption` that returns a cache key must return a key 

688 that uniquely identifies the complete state of this option, which will 

689 match any other :class:`.MapperOption` that itself retains the 

690 identical state. This includes path options, flags, etc. It should 

691 be a state that is repeatable and part of a limited set of possible 

692 options. 

693 

694 If the :class:`.MapperOption` does not apply to the given path and 

695 would not affect query results on such a path, it should return None, 

696 indicating the :class:`_query.Query` is safe to cache for this given 

697 loader path and that this :class:`.MapperOption` need not be 

698 part of the cache key. 

699 

700 

701 """ 

702 return False 

703 

704 

705class LoaderStrategy(object): 

706 """Describe the loading behavior of a StrategizedProperty object. 

707 

708 The ``LoaderStrategy`` interacts with the querying process in three 

709 ways: 

710 

711 * it controls the configuration of the ``InstrumentedAttribute`` 

712 placed on a class to handle the behavior of the attribute. this 

713 may involve setting up class-level callable functions to fire 

714 off a select operation when the attribute is first accessed 

715 (i.e. a lazy load) 

716 

717 * it processes the ``QueryContext`` at statement construction time, 

718 where it can modify the SQL statement that is being produced. 

719 For example, simple column attributes will add their represented 

720 column to the list of selected columns, a joined eager loader 

721 may establish join clauses to add to the statement. 

722 

723 * It produces "row processor" functions at result fetching time. 

724 These "row processor" functions populate a particular attribute 

725 on a particular mapped instance. 

726 

727 """ 

728 

729 __slots__ = ( 

730 "parent_property", 

731 "is_class_level", 

732 "parent", 

733 "key", 

734 "strategy_key", 

735 "strategy_opts", 

736 ) 

737 

738 def __init__(self, parent, strategy_key): 

739 self.parent_property = parent 

740 self.is_class_level = False 

741 self.parent = self.parent_property.parent 

742 self.key = self.parent_property.key 

743 self.strategy_key = strategy_key 

744 self.strategy_opts = dict(strategy_key) 

745 

746 def init_class_attribute(self, mapper): 

747 pass 

748 

749 def setup_query( 

750 self, context, query_entity, path, loadopt, adapter, **kwargs 

751 ): 

752 """Establish column and other state for a given QueryContext. 

753 

754 This method fulfills the contract specified by MapperProperty.setup(). 

755 

756 StrategizedProperty delegates its setup() method 

757 directly to this method. 

758 

759 """ 

760 

761 def create_row_processor( 

762 self, context, path, loadopt, mapper, result, adapter, populators 

763 ): 

764 """Establish row processing functions for a given QueryContext. 

765 

766 This method fulfills the contract specified by 

767 MapperProperty.create_row_processor(). 

768 

769 StrategizedProperty delegates its create_row_processor() method 

770 directly to this method. 

771 

772 """ 

773 

774 def __str__(self): 

775 return str(self.parent_property)