tabella di mapping SQLAlchemy con colonne non ASCII in classe
-
27-10-2019 - |
Domanda
item = Table('Item', metadata, autoload=True, autoload_with=engine, encoding = 'cp1257')
class Item(object):
pass
from sqlalchemy.orm import mapper
mapper(Item, item)
ottengo l'errore:
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)
Sto collegando a MSSQL. autoload tavolo sembra funzionare. Ho solo questo errore durante il tentativo di mappare. Grazie a tutti per l'aiuto!
Soluzione
Mappatura della tabella a una classe crea mappata immobili in classe. Le proprietà hanno lo stesso nome delle colonne, per impostazione predefinita. Dal momento che python 2.x permette solo identificatori ASCII, che non riesce se si hanno i nomi delle colonne non-ASCII.
L'unica soluzione che viene in mente è quello di dare gli identificatori un nome diverso quando la mappatura della tabella a una classe.
L'esempio che segue lo fa. Si noti che sto creando il tavolo sul codice per la semplicità, quindi chiunque può eseguire il codice senza dover tabella esistente. Ma si potrebbe fare lo stesso con un tavolo riflessa.
#-*- 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'ãéìöû']
})
Dopo di che è possibile utilizzare Foo.nome
per fare riferimento alla colonna nomé
e Foo.aeiou
di fare riferimento alla colonna ãéìöû
.
Altri suggerimenti
I affrontato lo stesso problema e finalmente riuscito a farlo sostituire tabella tasto [ 'colonna']. Dopo autoloading, basta fare tutte le classi il vostro tavolo ereditano questo e quindi modificare la sostituzione nome della colonna nel metodo mapTo o cambiarle manualmente nomi desiderati con un metodo di dizionario e columns_descriptor. Non so se questo non è il modo giusto per farlo, ma dopo aver cercato per ore è il migliore aproach che ho.
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
mi aspetto che sarà utile
ho scoperto che avrei potuto fare questo con una semplice aggiunta alla mia classe riflessa:
metadata = MetaData(bind=engine, reflect=True)
sm = sessionmaker(bind=engine)
class tblOrders(Base):
__table__ = metadata.tables['tblOrders']
meter = __table__.c['Meter#']
meter
viene mappato alla colonna Meter#
sottostante, che permette questo codice lavoro:
currOrder = tblOrders()
currOrder.meter = '5'
Senza la mappatura, pitone lo vede come una dichiarazione rotto becase Meter
seguito da un commento non esiste nell'oggetto.