Create a ``UNION`` statement used by a polymorphic mapper. See :ref:`concrete_inheritance` for an example of how this is used. :param table_map: mapping of polymorphic identities to :class:`_schema.Table` objects. :param typecolname: string name of a "discriminator" column, w
(
table_map, typecolname, aliasname="p_union", cast_nulls=True
)
| 328 | |
| 329 | |
| 330 | def polymorphic_union( |
| 331 | table_map, typecolname, aliasname="p_union", cast_nulls=True |
| 332 | ): |
| 333 | """Create a ``UNION`` statement used by a polymorphic mapper. |
| 334 | |
| 335 | See :ref:`concrete_inheritance` for an example of how |
| 336 | this is used. |
| 337 | |
| 338 | :param table_map: mapping of polymorphic identities to |
| 339 | :class:`_schema.Table` objects. |
| 340 | :param typecolname: string name of a "discriminator" column, which will be |
| 341 | derived from the query, producing the polymorphic identity for |
| 342 | each row. If ``None``, no polymorphic discriminator is generated. |
| 343 | :param aliasname: name of the :func:`~sqlalchemy.sql.expression.alias()` |
| 344 | construct generated. |
| 345 | :param cast_nulls: if True, non-existent columns, which are represented |
| 346 | as labeled NULLs, will be passed into CAST. This is a legacy behavior |
| 347 | that is problematic on some backends such as Oracle - in which case it |
| 348 | can be set to False. |
| 349 | |
| 350 | """ |
| 351 | |
| 352 | colnames: util.OrderedSet[str] = util.OrderedSet() |
| 353 | colnamemaps = {} |
| 354 | types = {} |
| 355 | for key in table_map: |
| 356 | table = table_map[key] |
| 357 | |
| 358 | table = coercions.expect(roles.FromClauseRole, table) |
| 359 | table_map[key] = table |
| 360 | |
| 361 | m = {} |
| 362 | for c in table.c: |
| 363 | if c.key == typecolname: |
| 364 | raise sa_exc.InvalidRequestError( |
| 365 | "Polymorphic union can't use '%s' as the discriminator " |
| 366 | "column due to mapped column %r; please apply the " |
| 367 | "'typecolname' " |
| 368 | "argument; this is available on " |
| 369 | "ConcreteBase as '_concrete_discriminator_name'" |
| 370 | % (typecolname, c) |
| 371 | ) |
| 372 | colnames.add(c.key) |
| 373 | m[c.key] = c |
| 374 | types[c.key] = c.type |
| 375 | colnamemaps[table] = m |
| 376 | |
| 377 | def col(name, table): |
| 378 | try: |
| 379 | return colnamemaps[table][name] |
| 380 | except KeyError: |
| 381 | if cast_nulls: |
| 382 | return sql.cast(sql.null(), types[name]).label(name) |
| 383 | else: |
| 384 | return sql.type_coerce(sql.null(), types[name]).label(name) |
| 385 | |
| 386 | result = [] |
| 387 | for type_, table in table_map.items(): |