Sqlalchemy Mapping-Tabelle mit Nicht-ASCII-Spalten bis zur Klasse
-
27-10-2019 - |
Frage
item = Table('Item', metadata, autoload=True, autoload_with=engine, encoding = 'cp1257')
class Item(object):
pass
from sqlalchemy.orm import mapper
mapper(Item, item)
Ich bekomme Fehler:
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)
Ich verbinde mich mit MSSQL. Tabelle Autoload scheint zu funktionieren. Ich erhalte diesen Fehler nur, während ich versuche zu kartieren. Vielen Dank für Hilfe!
Lösung
Durch die Zuordnung der Tabelle in eine Klasse werden kartierte Eigenschaften in der Klasse erstellt. Die Eigenschaften haben standardmäßig den gleichen Namen der Spalten. Da Python 2.x nur ASCII-Kennungen zulässt, schlägt dies fehl, wenn Sie nicht-ASCII-Spaltennamen haben.
Die einzige Lösung, die ich mir vorstellen kann, ist, den Kennern einen anderen Namen beim Abbau der Tabelle an eine Klasse zu geben.
Das folgende Beispiel macht das. Beachten Sie, dass ich die Tabelle im Code zum Einfachheit halber erstelle, damit jeder den Code ohne vorhandene Tabelle ausführen kann. Aber Sie könnten dasselbe mit einem reflektierten Tisch tun.
#-*- 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'ãéìöû']
})
Danach können Sie verwenden Foo.nome
sich auf die beziehen nomé
Säule und Foo.aeiou
sich auf die beziehen ãéìöû
Säule.
Andere Tipps
Ich hatte das gleiche Problem und schaffte es schließlich, dies zu ersetzen, um Tabelle ['Spalte'] zu ersetzen. Key nach dem Autoladieren, lass alle deine Tabellenklassen diesen erben und dann den Ersatz des Spaltennamens in der Mapto -Methode ändern oder die gewünschten Namen manuell mit überschreiben Eine Methode für Wörterbuch- und Spalten_Deskriptor. Ich weiß nicht, ob dies nicht der richtige Weg ist, aber nachdem ich nach Stunden gesucht habe, ist die beste Acroach, die ich habe.
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
Ich gehe davon aus, dass es nützlich sein wird,
Ich fand, dass ich dies mit einer einfachen Ergänzung zu meiner reflektierten Klasse tun konnte:
metadata = MetaData(bind=engine, reflect=True)
sm = sessionmaker(bind=engine)
class tblOrders(Base):
__table__ = metadata.tables['tblOrders']
meter = __table__.c['Meter#']
meter
ist jetzt dem zugrunde liegenden kartiert Meter#
Spalte, in der dieser Code funktioniert:
currOrder = tblOrders()
currOrder.meter = '5'
Ohne die Zuordnung sieht Python es als eine kaputte Aussage, weil Meter
gefolgt von einem Kommentar existiert nicht im Objekt.