Mapping Table Columns

The default behavior of mapper() is to assemble all the columns inthe mapped Table into mapped object attributes, each of which arenamed according to the name of the column itself (specifically, the keyattribute of Column). This behavior can bemodified in several ways.

Naming Columns Distinctly from Attribute Names

A mapping by default shares the same name for aColumn as that of the mapped attribute - specificallyit matches the Column.key attribute on Column, whichby default is the same as the Column.name.

The name assigned to the Python attribute which maps toColumn can be different from either Column.name or Column.keyjust by assigning it that way, as we illustrate here in a Declarative mapping:

  1. class User(Base):
  2. __tablename__ = 'user'
  3. id = Column('user_id', Integer, primary_key=True)
  4. name = Column('user_name', String(50))

Where above User.id resolves to a column named user_idand User.name resolves to a column named user_name.

When mapping to an existing table, the Column objectcan be referenced directly:

  1. class User(Base):
  2. __table__ = user_table
  3. id = user_table.c.user_id
  4. name = user_table.c.user_name

Or in a classical mapping, placed in the properties dictionarywith the desired key:

  1. mapper(User, user_table, properties={
  2. 'id': user_table.c.user_id,
  3. 'name': user_table.c.user_name,
  4. })

In the next section we’ll examine the usage of .key more closely.

Automating Column Naming Schemes from Reflected Tables

In the previous section Naming Columns Distinctly from Attribute Names, we showed howa Column explicitly mapped to a class can have a different attributename than the column. But what if we aren’t listing out Columnobjects explicitly, and instead are automating the production of Tableobjects using reflection (e.g. as described in Reflecting Database Objects)?In this case we can make use of the DDLEvents.column_reflect() eventto intercept the production of Column objects and provide themwith the Column.key of our choice:

  1. @event.listens_for(Table, "column_reflect")def column_reflect(inspector, table, column_info):

  2. # set column.key = "attr_<lower_case_name>"
  3. column_info[&#39;key&#39;] = &#34;attr_%s&#34; % column_info[&#39;name&#39;].lower()</pre>
  4. With the above event, the reflection of Column objects will be interceptedwith our event that adds a new “.key element, such as in a mapping as below:

  5. class MyClass(Base):
  6.     __table__ = Table("some_table", Base.metadata,
  7.                 autoload=True, autoload_with=some_engine)
  8.  
  9.  
  10.  
  11. If we want to qualify our event to only react for the specific MetaDataobject above, we can check for it in our event:

  12. @event.listens_for(Table, "column_reflect")def column_reflect(inspector, table, column_info):    if table.metadata is Base.metadata:

  13.     # set column.key = &#34;attr_&lt;lower_case_name&gt;&#34;
  14.     column_info[&#39;key&#39;] = &#34;attr_%s&#34; % column_info[&#39;name&#39;].lower()</pre>
  15. Naming All Columns with a Prefix

    A quick approach to prefix column names, typically when mappingto an existing Table object, is to use column_prefix:

  16. class User(Base):
  17.     __table__ = user_table
  18.     __mapper_args__ = {'column_prefix':'_'}
  19. The above will place attribute names such as _user_id, _user_name,_password etc. on the mapped User class.

  20. This approach is uncommon in modern usage. For dealing with reflectedtables, a more flexible approach is to use that described inAutomating Column Naming Schemes from Reflected Tables.

  21. Using column_property for column level options

    Options can be specified when mapping a Column using thecolumn_property() function. This functionexplicitly creates the ColumnProperty used by themapper() to keep track of the Column; normally, themapper() creates this automatically. Using column_property(),we can pass additional arguments about how wed like the Columnto be mapped. Below, we pass an option active_history,which specifies that a change to this columns value shouldresult in the former value being loaded first:

  22. from sqlalchemy.orm import column_property
  23. class User(Base):
  24.     __tablename__ = 'user'
  25.     id = Column(Integer, primary_key=True)
  26.     name = column_property(Column(String(50)), active_history=True)
  27. column_property() is also used to map a single attribute tomultiple columns. This use case arises when mapping to a join()which has attributes which are equated to each other:

  28. class User(Base):
  29.     __table__ = user.join(address)
  30.     # assign "user.id", "address.user_id" to the
  31.     # "id" attribute
  32.     id = column_property(user_table.c.id, address_table.c.user_id)
  33. For more examples featuring this usage, see Mapping a Class against Multiple Tables.

  34. Another place where column_property() is needed is to specify SQL expressions asmapped attributes, such as below where we create an attribute fullnamethat is the string concatenation of the firstname and lastnamecolumns:

  35. class User(Base):
  36.     __tablename__ = 'user'
  37.     id = Column(Integer, primary_key=True)
  38.     firstname = Column(String(50))
  39.     lastname = Column(String(50))
  40.     fullname = column_property(firstname + " " + lastname)
  41. See examples of this usage at SQL Expressions as Mapped Attributes.

      • sqlalchemy.orm.columnproperty(columns, *kwargs_)
      • Provide a column-level property for use with a Mapper.
    • Column-based properties can normally be applied to the mappersproperties dictionary using the Column element directly.Use this function when the given column is not directly present withinthe mappers selectable; examples include SQL expressions, functions,and scalar SELECT queries.

    • Columns that arent present in the mappers selectable wont bepersisted by the mapper and are effectively read-only attributes.

        • Parameters
            • *cols list of Column objects to be mapped.

            • active_history=False When True, indicates that the previous value for ascalar attribute should be loaded when replaced, if notalready loaded. Normally, history tracking logic forsimple non-primary-key scalar values only needs to beaware of the new value in order to perform a flush. Thisflag is available for applications that make use ofattributes.get_history() or Session.is_modified()which also need to knowthe previous value of the attribute.

            • comparator_factory a class which extendsColumnProperty.Comparator which provides custom SQLclause generation for comparison operations.

            • group a group name for this property when marked as deferred.

            • deferred when True, the column property is deferred”, meaning thatit does not load immediately, and is instead loaded when theattribute is first accessed on an instance. See alsodeferred().

            • doc optional string that will be applied as the doc on theclass-bound descriptor.

            • expire_on_flush=True Disable expiry on flush. A column_property() which refersto a SQL expression (and not a single table-bound column)is considered to be a read only property; populating ithas no effect on the state of data, and it can only returndatabase state. For this reason a column_property()’s valueis expired whenever the parent object is involved in aflush, that is, has any kind of dirty state within a flush.Setting this parameter to False will have the effect ofleaving any existing value present after the flush proceeds.Note however that the Session with default expirationsettings still expiresall attributes after a Session.commit() call, however.

            • info Optional data dictionary which will be populated into theMapperProperty.info attribute of this object.

            • extension

        • an AttributeExtension instance, or list of extensions,which will be prepended to the list of attribute listeners for theresulting descriptor placed on the class.

        • Deprecated since version 0.7: AttributeExtension is deprecated in favor of the AttributeEvents listener interface. The column_property.extension parameter will be removed in a future release.

        • Mapping a Subset of Table Columns

          Sometimes, a Table object was made available using thereflection process described at Reflecting Database Objects to loadthe tables structure from the database.For such a table that has lots of columns that dont need to be referencedin the application, the include_properties or exclude_propertiesarguments 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 includingthe 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, includingall columns present except street, city, state, and zip.

        • When this mapping is used, the columns that are not included will not bereferenced in any SELECT statements emitted by Query, nor will therebe any mapped attribute on the mapped class which represents the column;assigning an attribute of that name will have no effect beyond that ofa normal Python attribute assignment.

        • In some cases, multiple columns may have the same name, such as whenmapping to a join of two or more tables that share some column name.include_properties and exclude_properties can also accommodateColumn objects to more accurately describe which columnsshould 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 Columnobjects, i.e. those described at Column INSERT/UPDATE Defaults including thoseconfigured by the Column.default,Column.onupdate, Column.server_default andColumn.server_onupdate parameters, will continue to functionnormally even if those Column objects are not mapped. This isbecause in the case of Column.default andColumn.onupdate, the Column object is still presenton the underlying Table, thus allowing the default functions totake place when the ORM emits an INSERT or UPDATE, and in the case ofColumn.server_default and Column.server_onupdate,the relational database itself emits these defaults as a server sidebehavior.