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

206 statements  

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

1# ext/declarative/api.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"""Public API functions and helpers for declarative.""" 

8from __future__ import absolute_import 

9 

10import itertools 

11import re 

12import weakref 

13 

14from . import attributes 

15from . import clsregistry 

16from . import exc as orm_exc 

17from . import instrumentation 

18from . import interfaces 

19from . import mapper as mapperlib 

20from .base import _inspect_mapped_class 

21from .decl_base import _add_attribute 

22from .decl_base import _as_declarative 

23from .decl_base import _declarative_constructor 

24from .decl_base import _DeferredMapperConfig 

25from .decl_base import _del_attribute 

26from .decl_base import _mapper 

27from .descriptor_props import SynonymProperty as _orm_synonym 

28from .. import exc 

29from .. import inspection 

30from .. import util 

31from ..sql.schema import MetaData 

32from ..util import hybridmethod 

33from ..util import hybridproperty 

34 

35 

36def has_inherited_table(cls): 

37 """Given a class, return True if any of the classes it inherits from has a 

38 mapped table, otherwise return False. 

39 

40 This is used in declarative mixins to build attributes that behave 

41 differently for the base class vs. a subclass in an inheritance 

42 hierarchy. 

43 

44 .. seealso:: 

45 

46 :ref:`decl_mixin_inheritance` 

47 

48 """ 

49 for class_ in cls.__mro__[1:]: 

50 if getattr(class_, "__table__", None) is not None: 

51 return True 

52 return False 

53 

54 

55class DeclarativeMeta(type): 

56 def __init__(cls, classname, bases, dict_, **kw): 

57 # use cls.__dict__, which can be modified by an 

58 # __init_subclass__() method (#7900) 

59 dict_ = cls.__dict__ 

60 

61 # early-consume registry from the initial declarative base, 

62 # assign privately to not conflict with subclass attributes named 

63 # "registry" 

64 reg = getattr(cls, "_sa_registry", None) 

65 if reg is None: 

66 reg = dict_.get("registry", None) 

67 if not isinstance(reg, registry): 

68 raise exc.InvalidRequestError( 

69 "Declarative base class has no 'registry' attribute, " 

70 "or registry is not a sqlalchemy.orm.registry() object" 

71 ) 

72 else: 

73 cls._sa_registry = reg 

74 

75 if not cls.__dict__.get("__abstract__", False): 

76 _as_declarative(reg, cls, dict_) 

77 type.__init__(cls, classname, bases, dict_) 

78 

79 def __setattr__(cls, key, value): 

80 _add_attribute(cls, key, value) 

81 

82 def __delattr__(cls, key): 

83 _del_attribute(cls, key) 

84 

85 

86def synonym_for(name, map_column=False): 

87 """Decorator that produces an :func:`_orm.synonym` 

88 attribute in conjunction with a Python descriptor. 

89 

90 The function being decorated is passed to :func:`_orm.synonym` as the 

91 :paramref:`.orm.synonym.descriptor` parameter:: 

92 

93 class MyClass(Base): 

94 __tablename__ = 'my_table' 

95 

96 id = Column(Integer, primary_key=True) 

97 _job_status = Column("job_status", String(50)) 

98 

99 @synonym_for("job_status") 

100 @property 

101 def job_status(self): 

102 return "Status: %s" % self._job_status 

103 

104 The :ref:`hybrid properties <mapper_hybrids>` feature of SQLAlchemy 

105 is typically preferred instead of synonyms, which is a more legacy 

106 feature. 

107 

108 .. seealso:: 

109 

110 :ref:`synonyms` - Overview of synonyms 

111 

112 :func:`_orm.synonym` - the mapper-level function 

113 

114 :ref:`mapper_hybrids` - The Hybrid Attribute extension provides an 

115 updated approach to augmenting attribute behavior more flexibly than 

116 can be achieved with synonyms. 

117 

118 """ 

119 

120 def decorate(fn): 

121 return _orm_synonym(name, map_column=map_column, descriptor=fn) 

122 

123 return decorate 

124 

125 

126class declared_attr(interfaces._MappedAttribute, property): 

127 """Mark a class-level method as representing the definition of 

128 a mapped property or special declarative member name. 

129 

130 :class:`_orm.declared_attr` is typically applied as a decorator to a class 

131 level method, turning the attribute into a scalar-like property that can be 

132 invoked from the uninstantiated class. The Declarative mapping process 

133 looks for these :class:`_orm.declared_attr` callables as it scans classes, 

134 and assumes any attribute marked with :class:`_orm.declared_attr` will be a 

135 callable that will produce an object specific to the Declarative mapping or 

136 table configuration. 

137 

138 :class:`_orm.declared_attr` is usually applicable to mixins, to define 

139 relationships that are to be applied to different implementors of the 

140 class. It is also used to define :class:`_schema.Column` objects that 

141 include the :class:`_schema.ForeignKey` construct, as these cannot be 

142 easily reused across different mappings. The example below illustrates 

143 both:: 

144 

145 class ProvidesUser(object): 

146 "A mixin that adds a 'user' relationship to classes." 

147 

148 @declared_attr 

149 def user_id(self): 

150 return Column(ForeignKey("user_account.id")) 

151 

152 @declared_attr 

153 def user(self): 

154 return relationship("User") 

155 

156 :class:`_orm.declared_attr` can also be applied to mapped classes, such as 

157 to provide a "polymorphic" scheme for inheritance:: 

158 

159 class Employee(Base): 

160 id = Column(Integer, primary_key=True) 

161 type = Column(String(50), nullable=False) 

162 

163 @declared_attr 

164 def __tablename__(cls): 

165 return cls.__name__.lower() 

166 

167 @declared_attr 

168 def __mapper_args__(cls): 

169 if cls.__name__ == 'Employee': 

170 return { 

171 "polymorphic_on":cls.type, 

172 "polymorphic_identity":"Employee" 

173 } 

174 else: 

175 return {"polymorphic_identity":cls.__name__} 

176 

177 To use :class:`_orm.declared_attr` inside of a Python dataclass 

178 as discussed at :ref:`orm_declarative_dataclasses_declarative_table`, 

179 it may be placed directly inside the field metadata using a lambda:: 

180 

181 @dataclass 

182 class AddressMixin: 

183 __sa_dataclass_metadata_key__ = "sa" 

184 

185 user_id: int = field( 

186 init=False, metadata={"sa": declared_attr(lambda: Column(ForeignKey("user.id")))} 

187 ) 

188 user: User = field( 

189 init=False, metadata={"sa": declared_attr(lambda: relationship(User))} 

190 ) 

191 

192 :class:`_orm.declared_attr` also may be omitted from this form using a 

193 lambda directly, as in:: 

194 

195 user: User = field( 

196 init=False, metadata={"sa": lambda: relationship(User)} 

197 ) 

198 

199 .. seealso:: 

200 

201 :ref:`orm_mixins_toplevel` - illustrates how to use Declarative Mixins 

202 which is the primary use case for :class:`_orm.declared_attr` 

203 

204 :ref:`orm_declarative_dataclasses_mixin` - illustrates special forms 

205 for use with Python dataclasses 

206 

207 """ # noqa: E501 

208 

209 def __init__(self, fget, cascading=False): 

210 super(declared_attr, self).__init__(fget) 

211 self.__doc__ = fget.__doc__ 

212 self._cascading = cascading 

213 

214 def __get__(desc, self, cls): 

215 # the declared_attr needs to make use of a cache that exists 

216 # for the span of the declarative scan_attributes() phase. 

217 # to achieve this we look at the class manager that's configured. 

218 manager = attributes.manager_of_class(cls) 

219 if manager is None: 

220 if not re.match(r"^__.+__$", desc.fget.__name__): 

221 # if there is no manager at all, then this class hasn't been 

222 # run through declarative or mapper() at all, emit a warning. 

223 util.warn( 

224 "Unmanaged access of declarative attribute %s from " 

225 "non-mapped class %s" % (desc.fget.__name__, cls.__name__) 

226 ) 

227 return desc.fget(cls) 

228 elif manager.is_mapped: 

229 # the class is mapped, which means we're outside of the declarative 

230 # scan setup, just run the function. 

231 return desc.fget(cls) 

232 

233 # here, we are inside of the declarative scan. use the registry 

234 # that is tracking the values of these attributes. 

235 declarative_scan = manager.declarative_scan() 

236 assert declarative_scan is not None 

237 reg = declarative_scan.declared_attr_reg 

238 

239 if desc in reg: 

240 return reg[desc] 

241 else: 

242 reg[desc] = obj = desc.fget(cls) 

243 return obj 

244 

245 @hybridmethod 

246 def _stateful(cls, **kw): 

247 return _stateful_declared_attr(**kw) 

248 

249 @hybridproperty 

250 def cascading(cls): 

251 """Mark a :class:`.declared_attr` as cascading. 

252 

253 This is a special-use modifier which indicates that a column 

254 or MapperProperty-based declared attribute should be configured 

255 distinctly per mapped subclass, within a mapped-inheritance scenario. 

256 

257 .. warning:: 

258 

259 The :attr:`.declared_attr.cascading` modifier has several 

260 limitations: 

261 

262 * The flag **only** applies to the use of :class:`.declared_attr` 

263 on declarative mixin classes and ``__abstract__`` classes; it 

264 currently has no effect when used on a mapped class directly. 

265 

266 * The flag **only** applies to normally-named attributes, e.g. 

267 not any special underscore attributes such as ``__tablename__``. 

268 On these attributes it has **no** effect. 

269 

270 * The flag currently **does not allow further overrides** down 

271 the class hierarchy; if a subclass tries to override the 

272 attribute, a warning is emitted and the overridden attribute 

273 is skipped. This is a limitation that it is hoped will be 

274 resolved at some point. 

275 

276 Below, both MyClass as well as MySubClass will have a distinct 

277 ``id`` Column object established:: 

278 

279 class HasIdMixin(object): 

280 @declared_attr.cascading 

281 def id(cls): 

282 if has_inherited_table(cls): 

283 return Column( 

284 ForeignKey('myclass.id'), primary_key=True 

285 ) 

286 else: 

287 return Column(Integer, primary_key=True) 

288 

289 class MyClass(HasIdMixin, Base): 

290 __tablename__ = 'myclass' 

291 # ... 

292 

293 class MySubClass(MyClass): 

294 "" 

295 # ... 

296 

297 The behavior of the above configuration is that ``MySubClass`` 

298 will refer to both its own ``id`` column as well as that of 

299 ``MyClass`` underneath the attribute named ``some_id``. 

300 

301 .. seealso:: 

302 

303 :ref:`declarative_inheritance` 

304 

305 :ref:`mixin_inheritance_columns` 

306 

307 

308 """ 

309 return cls._stateful(cascading=True) 

310 

311 

312class _stateful_declared_attr(declared_attr): 

313 def __init__(self, **kw): 

314 self.kw = kw 

315 

316 def _stateful(self, **kw): 

317 new_kw = self.kw.copy() 

318 new_kw.update(kw) 

319 return _stateful_declared_attr(**new_kw) 

320 

321 def __call__(self, fn): 

322 return declared_attr(fn, **self.kw) 

323 

324 

325def declarative_mixin(cls): 

326 """Mark a class as providing the feature of "declarative mixin". 

327 

328 E.g.:: 

329 

330 from sqlalchemy.orm import declared_attr 

331 from sqlalchemy.orm import declarative_mixin 

332 

333 @declarative_mixin 

334 class MyMixin: 

335 

336 @declared_attr 

337 def __tablename__(cls): 

338 return cls.__name__.lower() 

339 

340 __table_args__ = {'mysql_engine': 'InnoDB'} 

341 __mapper_args__= {'always_refresh': True} 

342 

343 id = Column(Integer, primary_key=True) 

344 

345 class MyModel(MyMixin, Base): 

346 name = Column(String(1000)) 

347 

348 The :func:`_orm.declarative_mixin` decorator currently does not modify 

349 the given class in any way; it's current purpose is strictly to assist 

350 the :ref:`Mypy plugin <mypy_toplevel>` in being able to identify 

351 SQLAlchemy declarative mixin classes when no other context is present. 

352 

353 .. versionadded:: 1.4.6 

354 

355 .. seealso:: 

356 

357 :ref:`orm_mixins_toplevel` 

358 

359 :ref:`mypy_declarative_mixins` - in the 

360 :ref:`Mypy plugin documentation <mypy_toplevel>` 

361 

362 """ # noqa: E501 

363 

364 return cls 

365 

366 

367def declarative_base( 

368 bind=None, 

369 metadata=None, 

370 mapper=None, 

371 cls=object, 

372 name="Base", 

373 constructor=_declarative_constructor, 

374 class_registry=None, 

375 metaclass=DeclarativeMeta, 

376): 

377 r"""Construct a base class for declarative class definitions. 

378 

379 The new base class will be given a metaclass that produces 

380 appropriate :class:`~sqlalchemy.schema.Table` objects and makes 

381 the appropriate :func:`~sqlalchemy.orm.mapper` calls based on the 

382 information provided declaratively in the class and any subclasses 

383 of the class. 

384 

385 The :func:`_orm.declarative_base` function is a shorthand version 

386 of using the :meth:`_orm.registry.generate_base` 

387 method. That is, the following:: 

388 

389 from sqlalchemy.orm import declarative_base 

390 

391 Base = declarative_base() 

392 

393 Is equivalent to:: 

394 

395 from sqlalchemy.orm import registry 

396 

397 mapper_registry = registry() 

398 Base = mapper_registry.generate_base() 

399 

400 See the docstring for :class:`_orm.registry` 

401 and :meth:`_orm.registry.generate_base` 

402 for more details. 

403 

404 .. versionchanged:: 1.4 The :func:`_orm.declarative_base` 

405 function is now a specialization of the more generic 

406 :class:`_orm.registry` class. The function also moves to the 

407 ``sqlalchemy.orm`` package from the ``declarative.ext`` package. 

408 

409 

410 :param bind: An optional 

411 :class:`~sqlalchemy.engine.Connectable`, will be assigned 

412 the ``bind`` attribute on the :class:`~sqlalchemy.schema.MetaData` 

413 instance. 

414 

415 .. deprecated:: 1.4 The "bind" argument to declarative_base is 

416 deprecated and will be removed in SQLAlchemy 2.0. 

417 

418 :param metadata: 

419 An optional :class:`~sqlalchemy.schema.MetaData` instance. All 

420 :class:`~sqlalchemy.schema.Table` objects implicitly declared by 

421 subclasses of the base will share this MetaData. A MetaData instance 

422 will be created if none is provided. The 

423 :class:`~sqlalchemy.schema.MetaData` instance will be available via the 

424 ``metadata`` attribute of the generated declarative base class. 

425 

426 :param mapper: 

427 An optional callable, defaults to :func:`~sqlalchemy.orm.mapper`. Will 

428 be used to map subclasses to their Tables. 

429 

430 :param cls: 

431 Defaults to :class:`object`. A type to use as the base for the generated 

432 declarative base class. May be a class or tuple of classes. 

433 

434 :param name: 

435 Defaults to ``Base``. The display name for the generated 

436 class. Customizing this is not required, but can improve clarity in 

437 tracebacks and debugging. 

438 

439 :param constructor: 

440 Specify the implementation for the ``__init__`` function on a mapped 

441 class that has no ``__init__`` of its own. Defaults to an 

442 implementation that assigns \**kwargs for declared 

443 fields and relationships to an instance. If ``None`` is supplied, 

444 no __init__ will be provided and construction will fall back to 

445 cls.__init__ by way of the normal Python semantics. 

446 

447 :param class_registry: optional dictionary that will serve as the 

448 registry of class names-> mapped classes when string names 

449 are used to identify classes inside of :func:`_orm.relationship` 

450 and others. Allows two or more declarative base classes 

451 to share the same registry of class names for simplified 

452 inter-base relationships. 

453 

454 :param metaclass: 

455 Defaults to :class:`.DeclarativeMeta`. A metaclass or __metaclass__ 

456 compatible callable to use as the meta type of the generated 

457 declarative base class. 

458 

459 .. seealso:: 

460 

461 :class:`_orm.registry` 

462 

463 """ 

464 

465 if bind is not None: 

466 # util.deprecated_params does not work 

467 util.warn_deprecated_20( 

468 "The ``bind`` argument to declarative_base is " 

469 "deprecated and will be removed in SQLAlchemy 2.0.", 

470 ) 

471 

472 return registry( 

473 _bind=bind, 

474 metadata=metadata, 

475 class_registry=class_registry, 

476 constructor=constructor, 

477 ).generate_base( 

478 mapper=mapper, 

479 cls=cls, 

480 name=name, 

481 metaclass=metaclass, 

482 ) 

483 

484 

485class registry(object): 

486 """Generalized registry for mapping classes. 

487 

488 The :class:`_orm.registry` serves as the basis for maintaining a collection 

489 of mappings, and provides configurational hooks used to map classes. 

490 

491 The three general kinds of mappings supported are Declarative Base, 

492 Declarative Decorator, and Imperative Mapping. All of these mapping 

493 styles may be used interchangeably: 

494 

495 * :meth:`_orm.registry.generate_base` returns a new declarative base 

496 class, and is the underlying implementation of the 

497 :func:`_orm.declarative_base` function. 

498 

499 * :meth:`_orm.registry.mapped` provides a class decorator that will 

500 apply declarative mapping to a class without the use of a declarative 

501 base class. 

502 

503 * :meth:`_orm.registry.map_imperatively` will produce a 

504 :class:`_orm.Mapper` for a class without scanning the class for 

505 declarative class attributes. This method suits the use case historically 

506 provided by the 

507 :func:`_orm.mapper` classical mapping function. 

508 

509 .. versionadded:: 1.4 

510 

511 .. seealso:: 

512 

513 :ref:`orm_mapping_classes_toplevel` - overview of class mapping 

514 styles. 

515 

516 """ 

517 

518 def __init__( 

519 self, 

520 metadata=None, 

521 class_registry=None, 

522 constructor=_declarative_constructor, 

523 _bind=None, 

524 ): 

525 r"""Construct a new :class:`_orm.registry` 

526 

527 :param metadata: 

528 An optional :class:`_schema.MetaData` instance. All 

529 :class:`_schema.Table` objects generated using declarative 

530 table mapping will make use of this :class:`_schema.MetaData` 

531 collection. If this argument is left at its default of ``None``, 

532 a blank :class:`_schema.MetaData` collection is created. 

533 

534 :param constructor: 

535 Specify the implementation for the ``__init__`` function on a mapped 

536 class that has no ``__init__`` of its own. Defaults to an 

537 implementation that assigns \**kwargs for declared 

538 fields and relationships to an instance. If ``None`` is supplied, 

539 no __init__ will be provided and construction will fall back to 

540 cls.__init__ by way of the normal Python semantics. 

541 

542 :param class_registry: optional dictionary that will serve as the 

543 registry of class names-> mapped classes when string names 

544 are used to identify classes inside of :func:`_orm.relationship` 

545 and others. Allows two or more declarative base classes 

546 to share the same registry of class names for simplified 

547 inter-base relationships. 

548 

549 """ 

550 lcl_metadata = metadata or MetaData() 

551 if _bind: 

552 lcl_metadata.bind = _bind 

553 

554 if class_registry is None: 

555 class_registry = weakref.WeakValueDictionary() 

556 

557 self._class_registry = class_registry 

558 self._managers = weakref.WeakKeyDictionary() 

559 self._non_primary_mappers = weakref.WeakKeyDictionary() 

560 self.metadata = lcl_metadata 

561 self.constructor = constructor 

562 

563 self._dependents = set() 

564 self._dependencies = set() 

565 

566 self._new_mappers = False 

567 

568 with mapperlib._CONFIGURE_MUTEX: 

569 mapperlib._mapper_registries[self] = True 

570 

571 @property 

572 def mappers(self): 

573 """read only collection of all :class:`_orm.Mapper` objects.""" 

574 

575 return frozenset(manager.mapper for manager in self._managers).union( 

576 self._non_primary_mappers 

577 ) 

578 

579 def _set_depends_on(self, registry): 

580 if registry is self: 

581 return 

582 registry._dependents.add(self) 

583 self._dependencies.add(registry) 

584 

585 def _flag_new_mapper(self, mapper): 

586 mapper._ready_for_configure = True 

587 if self._new_mappers: 

588 return 

589 

590 for reg in self._recurse_with_dependents({self}): 

591 reg._new_mappers = True 

592 

593 @classmethod 

594 def _recurse_with_dependents(cls, registries): 

595 todo = registries 

596 done = set() 

597 while todo: 

598 reg = todo.pop() 

599 done.add(reg) 

600 

601 # if yielding would remove dependents, make sure we have 

602 # them before 

603 todo.update(reg._dependents.difference(done)) 

604 yield reg 

605 

606 # if yielding would add dependents, make sure we have them 

607 # after 

608 todo.update(reg._dependents.difference(done)) 

609 

610 @classmethod 

611 def _recurse_with_dependencies(cls, registries): 

612 todo = registries 

613 done = set() 

614 while todo: 

615 reg = todo.pop() 

616 done.add(reg) 

617 

618 # if yielding would remove dependencies, make sure we have 

619 # them before 

620 todo.update(reg._dependencies.difference(done)) 

621 

622 yield reg 

623 

624 # if yielding would remove dependencies, make sure we have 

625 # them before 

626 todo.update(reg._dependencies.difference(done)) 

627 

628 def _mappers_to_configure(self): 

629 return itertools.chain( 

630 ( 

631 manager.mapper 

632 for manager in list(self._managers) 

633 if manager.is_mapped 

634 and not manager.mapper.configured 

635 and manager.mapper._ready_for_configure 

636 ), 

637 ( 

638 npm 

639 for npm in list(self._non_primary_mappers) 

640 if not npm.configured and npm._ready_for_configure 

641 ), 

642 ) 

643 

644 def _add_non_primary_mapper(self, np_mapper): 

645 self._non_primary_mappers[np_mapper] = True 

646 

647 def _dispose_cls(self, cls): 

648 clsregistry.remove_class(cls.__name__, cls, self._class_registry) 

649 

650 def _add_manager(self, manager): 

651 self._managers[manager] = True 

652 if manager.registry is not None and manager.is_mapped: 

653 raise exc.ArgumentError( 

654 "Class '%s' already has a primary mapper defined. " 

655 % manager.class_ 

656 ) 

657 manager.registry = self 

658 

659 def configure(self, cascade=False): 

660 """Configure all as-yet unconfigured mappers in this 

661 :class:`_orm.registry`. 

662 

663 The configure step is used to reconcile and initialize the 

664 :func:`_orm.relationship` linkages between mapped classes, as well as 

665 to invoke configuration events such as the 

666 :meth:`_orm.MapperEvents.before_configured` and 

667 :meth:`_orm.MapperEvents.after_configured`, which may be used by ORM 

668 extensions or user-defined extension hooks. 

669 

670 If one or more mappers in this registry contain 

671 :func:`_orm.relationship` constructs that refer to mapped classes in 

672 other registries, this registry is said to be *dependent* on those 

673 registries. In order to configure those dependent registries 

674 automatically, the :paramref:`_orm.registry.configure.cascade` flag 

675 should be set to ``True``. Otherwise, if they are not configured, an 

676 exception will be raised. The rationale behind this behavior is to 

677 allow an application to programmatically invoke configuration of 

678 registries while controlling whether or not the process implicitly 

679 reaches other registries. 

680 

681 As an alternative to invoking :meth:`_orm.registry.configure`, the ORM 

682 function :func:`_orm.configure_mappers` function may be used to ensure 

683 configuration is complete for all :class:`_orm.registry` objects in 

684 memory. This is generally simpler to use and also predates the usage of 

685 :class:`_orm.registry` objects overall. However, this function will 

686 impact all mappings throughout the running Python process and may be 

687 more memory/time consuming for an application that has many registries 

688 in use for different purposes that may not be needed immediately. 

689 

690 .. seealso:: 

691 

692 :func:`_orm.configure_mappers` 

693 

694 

695 .. versionadded:: 1.4.0b2 

696 

697 """ 

698 mapperlib._configure_registries({self}, cascade=cascade) 

699 

700 def dispose(self, cascade=False): 

701 """Dispose of all mappers in this :class:`_orm.registry`. 

702 

703 After invocation, all the classes that were mapped within this registry 

704 will no longer have class instrumentation associated with them. This 

705 method is the per-:class:`_orm.registry` analogue to the 

706 application-wide :func:`_orm.clear_mappers` function. 

707 

708 If this registry contains mappers that are dependencies of other 

709 registries, typically via :func:`_orm.relationship` links, then those 

710 registries must be disposed as well. When such registries exist in 

711 relation to this one, their :meth:`_orm.registry.dispose` method will 

712 also be called, if the :paramref:`_orm.registry.dispose.cascade` flag 

713 is set to ``True``; otherwise, an error is raised if those registries 

714 were not already disposed. 

715 

716 .. versionadded:: 1.4.0b2 

717 

718 .. seealso:: 

719 

720 :func:`_orm.clear_mappers` 

721 

722 """ 

723 

724 mapperlib._dispose_registries({self}, cascade=cascade) 

725 

726 def _dispose_manager_and_mapper(self, manager): 

727 if "mapper" in manager.__dict__: 

728 mapper = manager.mapper 

729 

730 mapper._set_dispose_flags() 

731 

732 class_ = manager.class_ 

733 self._dispose_cls(class_) 

734 instrumentation._instrumentation_factory.unregister(class_) 

735 

736 def generate_base( 

737 self, 

738 mapper=None, 

739 cls=object, 

740 name="Base", 

741 metaclass=DeclarativeMeta, 

742 ): 

743 """Generate a declarative base class. 

744 

745 Classes that inherit from the returned class object will be 

746 automatically mapped using declarative mapping. 

747 

748 E.g.:: 

749 

750 from sqlalchemy.orm import registry 

751 

752 mapper_registry = registry() 

753 

754 Base = mapper_registry.generate_base() 

755 

756 class MyClass(Base): 

757 __tablename__ = "my_table" 

758 id = Column(Integer, primary_key=True) 

759 

760 The above dynamically generated class is equivalent to the 

761 non-dynamic example below:: 

762 

763 from sqlalchemy.orm import registry 

764 from sqlalchemy.orm.decl_api import DeclarativeMeta 

765 

766 mapper_registry = registry() 

767 

768 class Base(metaclass=DeclarativeMeta): 

769 __abstract__ = True 

770 registry = mapper_registry 

771 metadata = mapper_registry.metadata 

772 

773 __init__ = mapper_registry.constructor 

774 

775 The :meth:`_orm.registry.generate_base` method provides the 

776 implementation for the :func:`_orm.declarative_base` function, which 

777 creates the :class:`_orm.registry` and base class all at once. 

778 

779 See the section :ref:`orm_declarative_mapping` for background and 

780 examples. 

781 

782 :param mapper: 

783 An optional callable, defaults to :func:`~sqlalchemy.orm.mapper`. 

784 This function is used to generate new :class:`_orm.Mapper` objects. 

785 

786 :param cls: 

787 Defaults to :class:`object`. A type to use as the base for the 

788 generated declarative base class. May be a class or tuple of classes. 

789 

790 :param name: 

791 Defaults to ``Base``. The display name for the generated 

792 class. Customizing this is not required, but can improve clarity in 

793 tracebacks and debugging. 

794 

795 :param metaclass: 

796 Defaults to :class:`.DeclarativeMeta`. A metaclass or __metaclass__ 

797 compatible callable to use as the meta type of the generated 

798 declarative base class. 

799 

800 .. seealso:: 

801 

802 :ref:`orm_declarative_mapping` 

803 

804 :func:`_orm.declarative_base` 

805 

806 """ 

807 metadata = self.metadata 

808 

809 bases = not isinstance(cls, tuple) and (cls,) or cls 

810 

811 class_dict = dict(registry=self, metadata=metadata) 

812 if isinstance(cls, type): 

813 class_dict["__doc__"] = cls.__doc__ 

814 

815 if self.constructor: 

816 class_dict["__init__"] = self.constructor 

817 

818 class_dict["__abstract__"] = True 

819 if mapper: 

820 class_dict["__mapper_cls__"] = mapper 

821 

822 if hasattr(cls, "__class_getitem__"): 

823 

824 def __class_getitem__(cls, key): 

825 # allow generic classes in py3.9+ 

826 return cls 

827 

828 class_dict["__class_getitem__"] = __class_getitem__ 

829 

830 return metaclass(name, bases, class_dict) 

831 

832 def mapped(self, cls): 

833 """Class decorator that will apply the Declarative mapping process 

834 to a given class. 

835 

836 E.g.:: 

837 

838 from sqlalchemy.orm import registry 

839 

840 mapper_registry = registry() 

841 

842 @mapper_registry.mapped 

843 class Foo: 

844 __tablename__ = 'some_table' 

845 

846 id = Column(Integer, primary_key=True) 

847 name = Column(String) 

848 

849 See the section :ref:`orm_declarative_mapping` for complete 

850 details and examples. 

851 

852 :param cls: class to be mapped. 

853 

854 :return: the class that was passed. 

855 

856 .. seealso:: 

857 

858 :ref:`orm_declarative_mapping` 

859 

860 :meth:`_orm.registry.generate_base` - generates a base class 

861 that will apply Declarative mapping to subclasses automatically 

862 using a Python metaclass. 

863 

864 """ 

865 _as_declarative(self, cls, cls.__dict__) 

866 return cls 

867 

868 def as_declarative_base(self, **kw): 

869 """ 

870 Class decorator which will invoke 

871 :meth:`_orm.registry.generate_base` 

872 for a given base class. 

873 

874 E.g.:: 

875 

876 from sqlalchemy.orm import registry 

877 

878 mapper_registry = registry() 

879 

880 @mapper_registry.as_declarative_base() 

881 class Base(object): 

882 @declared_attr 

883 def __tablename__(cls): 

884 return cls.__name__.lower() 

885 id = Column(Integer, primary_key=True) 

886 

887 class MyMappedClass(Base): 

888 # ... 

889 

890 All keyword arguments passed to 

891 :meth:`_orm.registry.as_declarative_base` are passed 

892 along to :meth:`_orm.registry.generate_base`. 

893 

894 """ 

895 

896 def decorate(cls): 

897 kw["cls"] = cls 

898 kw["name"] = cls.__name__ 

899 return self.generate_base(**kw) 

900 

901 return decorate 

902 

903 def map_declaratively(self, cls): 

904 """Map a class declaratively. 

905 

906 In this form of mapping, the class is scanned for mapping information, 

907 including for columns to be associated with a table, and/or an 

908 actual table object. 

909 

910 Returns the :class:`_orm.Mapper` object. 

911 

912 E.g.:: 

913 

914 from sqlalchemy.orm import registry 

915 

916 mapper_registry = registry() 

917 

918 class Foo: 

919 __tablename__ = 'some_table' 

920 

921 id = Column(Integer, primary_key=True) 

922 name = Column(String) 

923 

924 mapper = mapper_registry.map_declaratively(Foo) 

925 

926 This function is more conveniently invoked indirectly via either the 

927 :meth:`_orm.registry.mapped` class decorator or by subclassing a 

928 declarative metaclass generated from 

929 :meth:`_orm.registry.generate_base`. 

930 

931 See the section :ref:`orm_declarative_mapping` for complete 

932 details and examples. 

933 

934 :param cls: class to be mapped. 

935 

936 :return: a :class:`_orm.Mapper` object. 

937 

938 .. seealso:: 

939 

940 :ref:`orm_declarative_mapping` 

941 

942 :meth:`_orm.registry.mapped` - more common decorator interface 

943 to this function. 

944 

945 :meth:`_orm.registry.map_imperatively` 

946 

947 """ 

948 return _as_declarative(self, cls, cls.__dict__) 

949 

950 def map_imperatively(self, class_, local_table=None, **kw): 

951 r"""Map a class imperatively. 

952 

953 In this form of mapping, the class is not scanned for any mapping 

954 information. Instead, all mapping constructs are passed as 

955 arguments. 

956 

957 This method is intended to be fully equivalent to the classic 

958 SQLAlchemy :func:`_orm.mapper` function, except that it's in terms of 

959 a particular registry. 

960 

961 E.g.:: 

962 

963 from sqlalchemy.orm import registry 

964 

965 mapper_registry = registry() 

966 

967 my_table = Table( 

968 "my_table", 

969 mapper_registry.metadata, 

970 Column('id', Integer, primary_key=True) 

971 ) 

972 

973 class MyClass: 

974 pass 

975 

976 mapper_registry.map_imperatively(MyClass, my_table) 

977 

978 See the section :ref:`orm_imperative_mapping` for complete background 

979 and usage examples. 

980 

981 :param class\_: The class to be mapped. Corresponds to the 

982 :paramref:`_orm.mapper.class_` parameter. 

983 

984 :param local_table: the :class:`_schema.Table` or other 

985 :class:`_sql.FromClause` object that is the subject of the mapping. 

986 Corresponds to the 

987 :paramref:`_orm.mapper.local_table` parameter. 

988 

989 :param \**kw: all other keyword arguments are passed to the 

990 :func:`_orm.mapper` function directly. 

991 

992 .. seealso:: 

993 

994 :ref:`orm_imperative_mapping` 

995 

996 :ref:`orm_declarative_mapping` 

997 

998 """ 

999 return _mapper(self, class_, local_table, kw) 

1000 

1001 

1002mapperlib._legacy_registry = registry() 

1003 

1004 

1005@util.deprecated_params( 

1006 bind=( 

1007 "2.0", 

1008 "The ``bind`` argument to as_declarative is " 

1009 "deprecated and will be removed in SQLAlchemy 2.0.", 

1010 ) 

1011) 

1012def as_declarative(**kw): 

1013 """ 

1014 Class decorator which will adapt a given class into a 

1015 :func:`_orm.declarative_base`. 

1016 

1017 This function makes use of the :meth:`_orm.registry.as_declarative_base` 

1018 method, by first creating a :class:`_orm.registry` automatically 

1019 and then invoking the decorator. 

1020 

1021 E.g.:: 

1022 

1023 from sqlalchemy.orm import as_declarative 

1024 

1025 @as_declarative() 

1026 class Base(object): 

1027 @declared_attr 

1028 def __tablename__(cls): 

1029 return cls.__name__.lower() 

1030 id = Column(Integer, primary_key=True) 

1031 

1032 class MyMappedClass(Base): 

1033 # ... 

1034 

1035 .. seealso:: 

1036 

1037 :meth:`_orm.registry.as_declarative_base` 

1038 

1039 """ 

1040 bind, metadata, class_registry = ( 

1041 kw.pop("bind", None), 

1042 kw.pop("metadata", None), 

1043 kw.pop("class_registry", None), 

1044 ) 

1045 

1046 return registry( 

1047 _bind=bind, metadata=metadata, class_registry=class_registry 

1048 ).as_declarative_base(**kw) 

1049 

1050 

1051@inspection._inspects(DeclarativeMeta) 

1052def _inspect_decl_meta(cls): 

1053 mp = _inspect_mapped_class(cls) 

1054 if mp is None: 

1055 if _DeferredMapperConfig.has_cls(cls): 

1056 _DeferredMapperConfig.raise_unmapped_for_cls(cls) 

1057 raise orm_exc.UnmappedClassError( 

1058 cls, 

1059 msg="Class %s has a deferred mapping on it. It is not yet " 

1060 "usable as a mapped class." % orm_exc._safe_cls_name(cls), 

1061 ) 

1062 return mp