sqlalchemy orm column name convention

霸气de小男生 提交于 2021-02-04 21:45:16

问题


How do I automatically map all of my fields of the forms my_column to corresponding columns MyColumn in the database?

class MyTable(Base):
    __tablename__ = 'MyTable'
    # what I have now:
    my_column_1 = Column('MyColumn1', Integer)
    # what I want: how to configure a convention mapping rule for this:
    my_column_1 = Column(Integer)

回答1:


You could provide your own metaclass that renames columns, before passing control to DeclarativeMeta. Another option would be to alter column names in a before_parent_attach event handler for Column, but that would affect all columns globally, compared to just the ones in mapped classes using a specific declarative base.

from sqlalchemy.ext.declarative.api import DeclarativeMeta
from sqlalchemy import Column

class RenamingDeclarativeMeta(DeclarativeMeta):
    def __init__(cls, name, bases, namespace):
        _rename_declared_columns(namespace)
        super().__init__(name, bases, namespace)

    def __setattr__(cls, key, value):
        if isinstance(value, Column):
            _undefer_column_name_only(key, value)
        super().__setattr__(key, value)

def to_camelcase(s):
    return ''.join([w.title() for w in s.split('_')])

def _undefer_column_name_only(key, column):
    if column.name is None:
        column.name = to_camelcase(key)

def _rename_declared_columns(namespace):
    for key, attr in namespace.items():
        if isinstance(attr, Column):
            _undefer_column_name_only(key, attr)

This omits some functionality, such as renaming columns contained in ColumnProperty and CompositeProperty attributes. In order to use it pass it as the metaclass to declarative_base():

In [2]: from sqlalchemy.ext.declarative import declarative_base

In [3]: Base = declarative_base(metaclass=RenamingDeclarativeMeta)

In [4]: from sqlalchemy import Column, Integer

In [5]: class Foo(Base):
   ...:     __tablename__ = 'foo'
   ...:     id = Column(Integer, primary_key=True)
   ...:     bar_baz_1 = Column(Integer, nullable=False)
   ...:     

In [6]: Foo.id
Out[6]: <sqlalchemy.orm.attributes.InstrumentedAttribute at 0x7ffb7f770e60>

In [7]: Foo.bar_baz_1
Out[7]: <sqlalchemy.orm.attributes.InstrumentedAttribute at 0x7ffb7dc47200>

In [8]: list(Foo.__table__.columns)
Out[8]: 
[Column('Id', Integer(), table=<foo>, key='id', primary_key=True, nullable=False),
 Column('BarBaz1', Integer(), table=<foo>, key='bar_baz_1', nullable=False)]

The other approach would be to globally rename all columns that are attached to Table objects using the event API:

from sqlalchemy import event

@event.listens_for(Column, 'before_parent_attach')
def receive_column_before_table_attach(column, table):
    # As dumb as it gets. Blindly modifies all columns.
    column.name = to_camelcase(column.name)

This has a major drawback: it will blindly rename columns in reflected tables as well, which is why I'd recommend using the metaclass approach. It's better contained and is less surprising.



来源:https://stackoverflow.com/questions/51886972/sqlalchemy-orm-column-name-convention

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!