Таблица сопоставления SQLAlchemy со столбцами, отличными от ascii, в класс

item = Table('Item', metadata, autoload=True, autoload_with=engine, encoding = 'cp1257')

class Item(object):
    pass

from sqlalchemy.orm import mapper
mapper(Item, item)

Я получаю сообщение об ошибке:

line 43, in <module>
    mapper(Item, item) 
  File "C:\Python27\lib\site-packages\sqlalchemy\orm\__init__.py", line 890, in mapper
    return Mapper(class_, local_table, *args, **params)
  File "C:\Python27\lib\site-packages\sqlalchemy\orm\mapper.py", line 211, in __init__
    self._configure_properties()
  File "C:\Python27\lib\site-packages\sqlalchemy\orm\mapper.py", line 578, in _configure_properties
    setparent=True)
  File "C:\Python27\lib\site-packages\sqlalchemy\orm\mapper.py", line 618, in _configure_property
    self._log("_configure_property(%s, %s)", key, prop.__class__.__name__)
  File "C:\Python27\lib\site-packages\sqlalchemy\orm\mapper.py", line 877, in _log
    (self.non_primary and "|non-primary" or "") + ") " + 
  File "C:\Python27\lib\site-packages\sqlalchemy\util.py", line 1510, in __get__
    obj.__dict__[self.__name__] = result = self.fget(obj)
  File "C:\Python27\lib\site-packages\sqlalchemy\sql\expression.py", line 3544, in description
    return self.name.encode('ascii', 'backslashreplace')
UnicodeDecodeError: 'ascii' codec can't decode byte 0xeb in position 7: ordinal not in range(128)

Я подключаюсь к MSSQL. автозагрузка таблицы работает. Я получаю эту ошибку только при попытке отобразить. Всем спасибо за помощь!


person Atoc    schedule 29.04.2011    source источник
comment
encoding='utf-8' добавьте это в свой create_engine   -  person Abdul Kader    schedule 29.04.2011
comment
В настоящее время я использую кодировку «cp1257», чтение имен столбцов работает с этой настройкой, с utf8 я даже не могу автоматически загружать свою таблицу.   -  person Atoc    schedule 30.04.2011


Ответы (3)


Сопоставление таблицы с классом создает сопоставленные свойства в классе. По умолчанию свойства имеют то же имя, что и столбцы. Поскольку python 2.x допускает только идентификаторы ascii, это не удается, если у вас есть имена столбцов, отличные от ascii.

Единственное решение, которое я могу придумать, - это дать идентификаторам другое имя при сопоставлении таблицы с классом.

Пример ниже делает это. Обратите внимание, что я создаю таблицу в коде для простоты, поэтому любой может запустить код, не имея существующей таблицы. Но вы можете сделать то же самое с отраженной таблицей.

#-*- coding:utf-8 -*-

import sqlalchemy as sa
import sqlalchemy.orm

engine = sa.create_engine('sqlite://', echo=True) # new memory-only database
metadata = sa.MetaData(bind=engine)

# create a table. This could be reflected from the database instead:
tb = sa.Table('foo', metadata, 
    sa.Column(u'id', sa.Integer, primary_key=True),
    sa.Column(u'nomé', sa.Unicode(100)),
    sa.Column(u'ãéìöû', sa.Unicode(100))
)    
tb.create()

class Foo(object):
    pass

# maps the table to the class, defining different property names 
# for some columns:
sa.orm.mapper(Foo, tb, properties={
    'nome': tb.c[u'nomé'],
    'aeiou': tb.c[u'ãéìöû']
})

После этого вы можете использовать Foo.nome для ссылки на столбец nomé и Foo.aeiou для ссылки на столбец ãéìöû.

person nosklo    schedule 29.04.2011
comment
Это работает, однако я думал об автомагии, потому что это решение, по крайней мере, для меня, побеждает использование автозагрузки. И если кто-то добавит новый столбец, мое приложение сломается. Так что, может быть, мне лучше использовать exclude_properties и include_properties или не использовать автозагрузку - person Atoc; 29.04.2011
comment
ссылка эта ссылка поможет всем, кто хочет автоматизировать решение выше. - person Atoc; 30.04.2011
comment
То же самое может быть достигнуто с помощью декларативного стиля, как показано в документах: docs.sqlalchemy.org/en/latest/orm/ - person Akasha; 25.07.2014
comment
autoload имеет больше проблем, он пытается загрузить все отношения и для большой базы данных это занимает много времени (> 10 минут) - person Atoc; 04.01.2017

Я столкнулся с той же проблемой и, наконец, смог сделать это, заменив table['column'].key после его автоматической загрузки, просто сделайте так, чтобы все ваши классы таблиц наследовали этот класс, а затем измените замену имени столбца в методе mapTo или вручную переопределите нужные имена с помощью словарь и метод columns_descriptor. Я не знаю, правильный ли это способ сделать это, но после нескольких часов поиска это лучший подход, который у меня есть.

class SageProxy(object):

@classmethod
def ismapped(cls, table_name=None):
    if mappings:
        if table_name:
            if mappings.has_key(table_name):
                tmap=mappings[table_name]
                if tmap.has_key('class'):
                    tclass=tmap['class']
                    if tclass is cls:
                        return True
        else:
            for m in mappings:
                if cls is m['class']:
                    return True
    return False

@classmethod
def mappingprops(cls):
    #override this to pass properties to sqlalchemy mapper function
    return None

@classmethod
def columns_descriptors(cls):
    #override this to map columns to different class properties names
    #return dictionary where key is the column name and value is the desired property name
    return {}

@classmethod
def mapTo(cls, table_name, map_opts=None):
    if not cls.ismapped(table_name):
        tab_obj=Table(table_name,sage_md,autoload=True)
        for c in tab_obj.c:
            #clean field names
            tab_obj.c[c.name].key=c.key.replace(u'%',u'Porcentaje').replace(u'ñ',u'ny').replace(u'Ñ',u'NY').replace(u'-',u'_') 
        for k,v in cls.columns_descriptors():
            if tab_obj.c[k]:
                tab_obj.c[k].key=v
        mapper(cls, tab_obj, properties=cls.mappingprops())
        mappings[table_name]={'table':tab_obj,'class':cls}
    return cls

Я ожидаю, что это будет полезно

person Kelbethor    schedule 17.11.2011

Я обнаружил, что могу сделать это с помощью простого добавления к моему отраженному классу:

metadata = MetaData(bind=engine, reflect=True)
sm = sessionmaker(bind=engine)

class tblOrders(Base):
    __table__ = metadata.tables['tblOrders']
    meter = __table__.c['Meter#']

meter теперь сопоставляется с базовым столбцом Meter#, что позволяет этому коду работать:

currOrder = tblOrders()
currOrder.meter = '5'

Без сопоставления python видит это как неработающий оператор, потому что Meter, за которым следует комментарий, не существует в объекте.

person Spencer Rathbun    schedule 24.02.2012