Types of Mappings
Modern SQLAlchemy features two distinct styles of mapper configuration.The “Classical” style is SQLAlchemy’s original mapping API, whereas“Declarative” is the richer and more succinct system that builds on topof “Classical”. Both styles may be used interchangeably, as the endresult of each is exactly the same - a user-defined class mapped by themapper()
function onto a selectable unit, typically a Table
.
Declarative Mapping
The Declarative Mapping is the typical way thatmappings are constructed in modern SQLAlchemy.Making use of the Declarativesystem, the components of the user-defined class as well as theTable
metadata to which the class is mapped are definedat once:
- from sqlalchemy.ext.declarative import declarative_base
- from sqlalchemy import Column, Integer, String, ForeignKey
- Base = declarative_base()
- class User(Base):
- __tablename__ = 'user'
- id = Column(Integer, primary_key=True)
- name = Column(String)
- fullname = Column(String)
- nickname = Column(String)
Above, a basic single-table mapping with four columns. Additionalattributes, such as relationships to other mapped classes, are alsodeclared inline within the class definition:
- class User(Base):
- __tablename__ = 'user'
- id = Column(Integer, primary_key=True)
- name = Column(String)
- fullname = Column(String)
- nickname = Column(String)
- addresses = relationship("Address", backref="user", order_by="Address.id")
- class Address(Base):
- __tablename__ = 'address'
- id = Column(Integer, primary_key=True)
- user_id = Column(ForeignKey('user.id'))
- email_address = Column(String)
The declarative mapping system is introduced in theObject Relational Tutorial. For additional details on how this systemworks, see Declarative.
Classical Mappings
A Classical Mapping refers to the configuration of a mapped class using themapper()
function, without using the Declarative system. This isSQLAlchemy’s original class mapping API, and is still the base mappingsystem provided by the ORM.
In “classical” form, the table metadata is created separately with theTable
construct, then associated with the User
class viathe mapper()
function:
- from sqlalchemy import Table, MetaData, Column, Integer, String, ForeignKey
- from sqlalchemy.orm import mapper
- metadata = MetaData()
- user = Table('user', metadata,
- Column('id', Integer, primary_key=True),
- Column('name', String(50)),
- Column('fullname', String(50)),
- Column('nickname', String(12))
- )
- class User(object):
- def __init__(self, name, fullname, nickname):
- self.name = name
- self.fullname = fullname
- self.nickname = nickname
- mapper(User, user)
Information about mapped attributes, such as relationships to other classes, are providedvia the properties
dictionary. The example below illustrates a second Table
object, mapped to a class called Address
, then linked to User
via relationship()
:
- address = Table('address', metadata,
- Column('id', Integer, primary_key=True),
- Column('user_id', Integer, ForeignKey('user.id')),
- Column('email_address', String(50))
- )
- mapper(User, user, properties={
- 'addresses' : relationship(Address, backref='user', order_by=address.c.id)
- })
- mapper(Address, address)
When using classical mappings, classes must be provided directly without the benefitof the “string lookup” system provided by Declarative. SQL expressions are typicallyspecified in terms of the Table
objects, i.e. address.c.id
abovefor the Address
relationship, and not Address.id
, as Address
may notyet be linked to table metadata, nor can we specify a string here.
Some examples in the documentation still use the classical approach, but note thatthe classical as well as Declarative approaches are fully interchangeable. Bothsystems ultimately create the same configuration, consisting of a Table
,user-defined class, linked together with a mapper()
. When we talk about“the behavior of mapper()
”, this includes when using the Declarative systemas well - it’s still used, just behind the scenes.
Runtime Introspection of Mappings, Objects
The Mapper
object is available from any mapped class, regardlessof method, using the Runtime Inspection API system. Using theinspect()
function, one can acquire the Mapper
from amapped class:
- >>> from sqlalchemy import inspect
- >>> insp = inspect(User)
Detailed information is available including Mapper.columns
:
- >>> insp.columns
- <sqlalchemy.util._collections.OrderedProperties object at 0x102f407f8>
This is a namespace that can be viewed in a list format orvia individual names:
- >>> list(insp.columns)
- [Column('id', Integer(), table=<user>, primary_key=True, nullable=False), Column('name', String(length=50), table=<user>), Column('fullname', String(length=50), table=<user>), Column('nickname', String(length=50), table=<user>)]
- >>> insp.columns.name
- Column('name', String(length=50), table=<user>)
Other namespaces include Mapper.all_orm_descriptors
, which includes all mappedattributes as well as hybrids, association proxies:
- >>> insp.all_orm_descriptors
- <sqlalchemy.util._collections.ImmutableProperties object at 0x1040e2c68>
- >>> insp.all_orm_descriptors.keys()
- ['fullname', 'nickname', 'name', 'id']
As well as Mapper.column_attrs
:
- >>> list(insp.column_attrs)
- [<ColumnProperty at 0x10403fde0; id>, <ColumnProperty at 0x10403fce8; name>, <ColumnProperty at 0x1040e9050; fullname>, <ColumnProperty at 0x1040e9148; nickname>]
- >>> insp.column_attrs.name
- <ColumnProperty at 0x10403fce8; name>
- >>> insp.column_attrs.name.expression
- Column('name', String(length=50), table=<user>)
See also