SQLAlchemy table de correspondance avec des colonnes non-ASCII en classe
-
27-10-2019 - |
Question
item = Table('Item', metadata, autoload=True, autoload_with=engine, encoding = 'cp1257')
class Item(object):
pass
from sqlalchemy.orm import mapper
mapper(Item, item)
J'obtiens l'erreur:
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)
Je me connecte à MSSQL. Table autoload semble fonctionner. Je ne reçois que cette erreur en essayant de la carte. Merci à tous de l'aide!
La solution
Cartographie de la table à une classe crée des propriétés mappées sur la classe. Les propriétés ont le même nom des colonnes, par défaut. Depuis 2.x python ne permet que des identifiants de ascii, qui échoue si vous avez des noms de colonnes non-ascii.
La seule solution que je peux penser est de donner les identifiants un nom différent lors du mappage de la table à une classe.
L'exemple fait en dessous. Notez que je crée la table sur le code pour la simplicité, tout le monde peut exécuter le code sans avoir table existante. Mais vous pouvez faire la même chose avec une table réfléchie.
#-*- 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'ãéìöû']
})
Après cela, vous pouvez utiliser Foo.nome
pour se référer à la colonne nomé
et Foo.aeiou
de se référer à la colonne ãéìöû
.
Autres conseils
Je fait face au même problème et a finalement réussi à le faire remplacer la table [ « colonne »] clé. Après autoloading, juste faire toutes vos classes de table héritent celui-ci et ensuite modifier le remplacement du nom de la colonne dans la méthode mapTo ou changer manuellement Les noms désirés avec une méthode de dictionnaire et columns_descriptor. Je ne sais pas si ce n'est pas la bonne façon de le faire, mais après avoir cherché pendant des heures est le meilleur aproche que j'ai.
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
Je pense qu'il sera utile
J'ai trouvé que je pouvais le faire avec un simple ajout à ma classe reflète:
metadata = MetaData(bind=engine, reflect=True)
sm = sessionmaker(bind=engine)
class tblOrders(Base):
__table__ = metadata.tables['tblOrders']
meter = __table__.c['Meter#']
meter
est maintenant mis en correspondance avec la colonne de Meter#
sous-jacente, qui permet ce code au travail:
currOrder = tblOrders()
currOrder.meter = '5'
Sans la mise en correspondance, python, il voit comme une déclaration brisée becase Meter
suivi d'un commentaire n'existe pas dans l'objet.