TaiwanGrapefruitTea
TaiwanGrapefruitTea

Reputation: 1235

When the sqlalchemy ORM class attributes are different from database columns, how to get a list of the ORM class attributes?

Let's say you want to iterate over the ORM attributes of an ORM class in sqlalchemy. So, you want a list of the ORM attributes. How do you get that list?

If the ORM class does not rename the attributes and thus the ORM attributes match the database columns, then you can use the solution from: https://stackoverflow.com/a/24748320/1023033 (btw, there is also a built-in (private) function _orm_columns() in the source code file /lib/sqlalchemy/orm/base.py that seems to provide this functionality)

But if the python ORM class has different names than the database columns (for example in these 3 ORM attributes):

>>> class User(Base):
...     __tablename__ = 'users'
...
...     id = Column('pkey', Integer, primary_key=True)
...     name = Column('user_name', String)
...     fullname = Column('human_name', String)

then that method does not work. So, how do you do get the python version of the ORM attributes?

Upvotes: 1

Views: 5135

Answers (3)

Mark Frey
Mark Frey

Reputation: 1

Modifications to the mixin class / class method approach from davidsim to return a list of the attribute names while filtering out sqlalchemy.orm.RelationshipProperty attribute types (e.g., foreign models that link to the model class?):

import inspect

import sqlalchemy

class orm_mixin_class(object):
    """
    Additional functionality wanted in orm classes (that inherit from Base).
    """

    @classmethod
    def col_names(cls):
        """
        Get info about the orm class (as opposed to the database table it is mapped to).

        Returns:
            list: orm class attribute names
        """
        column_names = []
        member_pairs = inspect.getmembers(cls)
        for name, type in member_pairs:
            try:
                inspected_type = sqlalchemy.inspection.inspect(type)
                if isinstance(
                    inspected_type, sqlalchemy.orm.attributes.QueryableAttribute
                ) and not isinstance(
                    inspected_type.property, sqlalchemy.orm.RelationshipProperty
                ):
                    column_names.append(name)
            except Exception:
                pass

        return column_names

Then modified my base class as:

from sqlalchemy.orm import DeclarativeBase

class Base(DeclarativeBase, orm_mixin_class):
    [...]

Upvotes: 0

zzzeek
zzzeek

Reputation: 75117

This is already implemented using the inspection system:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import inspect

Base = declarative_base()

class A(Base):
    __tablename__ = 'a'
    id = Column(Integer, primary_key=True)
    x = Column(Integer)
    y = Column(Integer)

print inspect(A).c
print inspect(A).c.x
print inspect(A).column_attrs
print inspect(A).column_attrs.x
print inspect(A).column_attrs.x.expression

http://docs.sqlalchemy.org/en/latest/core/inspection.html

http://docs.sqlalchemy.org/en/latest/orm/mapping_api.html#sqlalchemy.orm.mapper.Mapper.all_orm_descriptors

http://docs.sqlalchemy.org/en/latest/orm/mapping_api.html#sqlalchemy.orm.mapper.Mapper.columns

http://docs.sqlalchemy.org/en/latest/orm/mapping_api.html#sqlalchemy.orm.mapper.Mapper.column_attrs

http://docs.sqlalchemy.org/en/latest/orm/mapping_api.html#sqlalchemy.orm.mapper.Mapper.c

Upvotes: 5

TaiwanGrapefruitTea
TaiwanGrapefruitTea

Reputation: 1235

I defined a mixin class to augment

Base = declarative_base()

with the mixin class defind as:

import inspect
import sqlalchemy as sqla 

class orm_mixin_class(object):
    """Additional functionality wanted in orm classes (that inherit from Base)."""

    @classmethod
    def orm_class_info(cls):
        """
        Get info about the orm class (as opposed to the database table it is mapped to).

        Returns:
            list of dict where each dict describes a orm class attribute.  
            Keys: s_class_attribute_name
        """
        o_leaf_level_class = cls   # this turns out the be the leaf class instead of this mixin class
        l_orm_attribute_pairs = inspect.getmembers(o_leaf_level_class)

        l_orm_class_info_dicts = []
        for (s_class_attribute_name, o_attr_type) in l_orm_attribute_pairs:
            d_attr_info = {}
            b_orm_attribute = False
            try:
                o_inspect = sqla.inspection.inspect(o_attr_type) 

                if isinstance(o_inspect, sqla.orm.attributes.QueryableAttribute):
                    b_orm_attribute = True
                    d_attr_info['s_class_attribute_name'] = s_class_attribute_name
            except:
                pass            

            if b_orm_attribute:
                # only orm attributes have an entry in the output list
                l_orm_class_info_dicts.append(d_attr_info)

        return(l_orm_class_info_dicts)

so the ORM attribute list can be easily obtained from a method call.

The ORM class declaration is now:

class User(Base, orm_mixin_class):

Upvotes: 0

Related Questions