The default behavior of mapper() is to assemble all the columns in
the mapped Table into mapped object attributes, each of which are
named according to the name of the column itself (specifically, the key
attribute of Column). This behavior can be
modified in several ways.
A mapping by default shares the same name for a
Column as that of the mapped attribute - specifically
it matches the Column.key attribute on Column, which
by default is the same as the Column.name.
The name assigned to the Python attribute which maps to
Column can be different from either Column.name or Column.key
just by assigning it that way, as we illustrate here in a Declarative mapping:
class User(Base):
__tablename__ = 'user'
id = Column('user_id', Integer, primary_key=True)
name = Column('user_name', String(50))Where above User.id resolves to a column named user_id
and User.name resolves to a column named user_name.
When mapping to an existing table, the Column object
can be referenced directly:
class User(Base):
__table__ = user_table
id = user_table.c.user_id
name = user_table.c.user_nameOr in a classical mapping, placed in the properties dictionary
with the desired key:
mapper(User, user_table, properties={
'id': user_table.c.user_id,
'name': user_table.c.user_name,
})In the next section we’ll examine the usage of .key more closely.
In the previous section Naming Columns Distinctly from Attribute Names, we showed how
a Column explicitly mapped to a class can have a different attribute
name than the column. But what if we aren’t listing out Column
objects explicitly, and instead are automating the production of Table
objects using reflection (e.g. as described in Reflecting Database Objects)?
In this case we can make use of the DDLEvents.column_reflect() event
to intercept the production of Column objects and provide them
with the Column.key of our choice:
@event.listens_for(Table, "column_reflect")
def column_reflect(inspector, table, column_info):
# set column.key = "attr_<lower_case_name>"
column_info['key'] = "attr_%s" % column_info['name'].lower()With the above event, the reflection of Column objects will be intercepted
with our event that adds a new “.key” element, such as in a mapping as below:
class MyClass(Base):
__table__ = Table("some_table", Base.metadata,
autoload=True, autoload_with=some_engine)If we want to qualify our event to only react for the specific MetaData
object above, we can check for it in our event:
@event.listens_for(Table, "column_reflect")
def column_reflect(inspector, table, column_info):
if table.metadata is Base.metadata:
# set column.key = "attr_<lower_case_name>"
column_info['key'] = "attr_%s" % column_info['name'].lower()A quick approach to prefix column names, typically when mapping
to an existing Table object, is to use column_prefix:
class User(Base):
__table__ = user_table
__mapper_args__ = {'column_prefix':'_'}The above will place attribute names such as _user_id, _user_name,
_password etc. on the mapped User class.
This approach is uncommon in modern usage. For dealing with reflected tables, a more flexible approach is to use that described in Automating Column Naming Schemes from Reflected Tables.
Options can be specified when mapping a Column using the
column_property() function. This function
explicitly creates the ColumnProperty used by the
mapper() to keep track of the Column; normally, the
mapper() creates this automatically. Using column_property(),
we can pass additional arguments about how we’d like the Column
to be mapped. Below, we pass an option active_history,
which specifies that a change to this column’s value should
result in the former value being loaded first:
from sqlalchemy.orm import column_property
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = column_property(Column(String(50)), active_history=True)column_property() is also used to map a single attribute to
multiple columns. This use case arises when mapping to a join()
which has attributes which are equated to each other:
class User(Base):
__table__ = user.join(address)
# assign "user.id", "address.user_id" to the
# "id" attribute
id = column_property(user_table.c.id, address_table.c.user_id)For more examples featuring this usage, see Mapping a Class against Multiple Tables.
Another place where column_property() is needed is to specify SQL expressions as
mapped attributes, such as below where we create an attribute fullname
that is the string concatenation of the firstname and lastname
columns:
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
firstname = Column(String(50))
lastname = Column(String(50))
fullname = column_property(firstname + " " + lastname)See examples of this usage at SQL Expressions as Mapped Attributes.
sqlalchemy.orm.column_property(*columns, **kwargs)¶Provide a column-level property for use with a Mapper.
Column-based properties can normally be applied to the mapper’s
properties dictionary using the Column element directly.
Use this function when the given column is not directly present within
the mapper’s selectable; examples include SQL expressions, functions,
and scalar SELECT queries.
Columns that aren’t present in the mapper’s selectable won’t be persisted by the mapper and are effectively “read-only” attributes.
| Parameters: |
|
|---|
Sometimes, a Table object was made available using the
reflection process described at Reflecting Database Objects to load
the table’s structure from the database.
For such a table that has lots of columns that don’t need to be referenced
in the application, the include_properties or exclude_properties
arguments can specify that only a subset of columns should be mapped.
For example:
class User(Base):
__table__ = user_table
__mapper_args__ = {
'include_properties' :['user_id', 'user_name']
}…will map the User class to the user_table table, only including
the user_id and user_name columns - the rest are not referenced.
Similarly:
class Address(Base):
__table__ = address_table
__mapper_args__ = {
'exclude_properties' : ['street', 'city', 'state', 'zip']
}…will map the Address class to the address_table table, including
all columns present except street, city, state, and zip.
When this mapping is used, the columns that are not included will not be
referenced in any SELECT statements emitted by Query, nor will there
be any mapped attribute on the mapped class which represents the column;
assigning an attribute of that name will have no effect beyond that of
a normal Python attribute assignment.
In some cases, multiple columns may have the same name, such as when
mapping to a join of two or more tables that share some column name.
include_properties and exclude_properties can also accommodate
Column objects to more accurately describe which columns
should be included or excluded:
class UserAddress(Base):
__table__ = user_table.join(addresses_table)
__mapper_args__ = {
'exclude_properties' :[address_table.c.id],
'primary_key' : [user_table.c.id]
}Note
insert and update defaults configured on individual
Column objects, i.e. those described at Column Insert/Update Defaults
including those configured by the default, update,
server_default and server_onupdate arguments, will continue to
function normally even if those Column objects are not mapped.
This is because in the case of default and update, the
Column object is still present on the underlying
Table, thus allowing the default functions to take place when
the ORM emits an INSERT or UPDATE, and in the case of server_default
and server_onupdate, the relational database itself maintains these
functions.