Am Samstag, 5. Januar 2013 18:50:45 UTC+1 schrieb Michael Bayer: > > > On Jan 5, 2013, at 7:42 AM, Alexander Jacob wrote: > > @compiles(i18NColumnClause) > def compile_i18n_column_clause(element, compiler, **kw): > return '%s_%s' % (element.name, GLOBAL_LANGUAGE) > > # custom type > class i18NType(sqlalchemy.types.TypeDecorator): > impl = sqlalchemy.types.Unicode > def column_expression(self, colexpr): > return i18NColumnClause(colexpr) > > # test class for custom type > class Country(Base): > > __tablename__ = 'country' > id = Column(Integer, primary_key=True) > iso2 = Column(String(2)) > name = Column(i18NType(64)) > > def __repr__(self): > return "<Country(%d/%s/%s)>" % (self.id, self.iso2, self.name) > > > Thank you for the answer.
> >> Interesting approach with the column_expression(), though that seems like >> it would circumvent the labeling behavior of the compiler and cause more >> complex expressions to fail. The INSERT case might work if you also >> implemented bind_expression() but I'm not sure if that approach overall >> doesn't break too much of the existing compilation functionality. >> > You are right, it causes problems when doing complex queries like joinedloads session.query(IM).options(joinedload(IM.country)).get(4) the generated sql gets broken like this: %(57544816 country)s.name_en AS country_1_name and then fails here File "C:\Python27\lib\site-packages\sqlalchemy\engine\default.py", line 320, in do_execute cursor.execute(statement, parameters) File "C:\Python27\lib\site-packages\MySQLdb\cursors.py", line 159, in execute query = query % db.literal(args) TypeError: format requires a mapping > > A more direct route with @compiles would be to compile Column, but still > tricky, in particular that the compilation process for a Column is very > involved. At the very least, for that to work it would have to be like > this: > > class i18NColumn(Column): # note it has to be Column, not ColumnClause > pass > > @compiles(i18NColumn) > def compile(element, compiler, **kw): > text = compile.visit_column(element, **kw) > text = text.replace(element.name, '%s_%s' %(element.name, > GLOBAL_LANGUAGE)) > return text > > so that existing labeling rules are not circumvented. but even that I'm > not 100% sure might not have some gotchas. > I will try that > Another variant on this would be to use the before_cursor_execute event to > just do a search and replace on the SQL statement before it's passed to the > DBAPI, I sometimes recommend that as well. Giving the "fake" column a name > that clearly indicates internationalization, like Column("name_<i18N>"), is > a good way to give a statement filter a clear path to where those names > would go. If you need this translation behavior everywhere, that might be > the best approach. > I preferred the event solution but class Country(Base): __tablename__ = 'country' name = Column('name_<I18N>', String(64)) 'SELECT country.`name_<I18N>` AS `country_name_<I18N>`, country.id AS country_id, country.iso2 AS country_iso2 FROM country WHERE country.id = %s' gets properly replace in before_cursor_execute to 'SELECT country.`name_en` AS `country_name_en`, country.id AS country_id, country.iso2 AS country_iso2 FROM country WHERE country.id = %s' and finally fails here: Traceback (most recent call last): File "C:\Program Files (x86)\JetBrains\PyCharm 2.5.2\helpers\pydev\pydevd.py", line 1401, in <module> debugger.run(setup['file'], None, None) File "C:\Program Files (x86)\JetBrains\PyCharm 2.5.2\helpers\pydev\pydevd.py", line 1055, in run pydev_imports.execfile(file, globals, locals) #execute the script File "C:/Projekte/interimexcellence/prototype/sqlalchemy/test.py", line 68, in <module> print germany File "C:/Projekte/interimexcellence/prototype/sqlalchemy/test.py", line 44, in __repr__ return "<Country(%d/%s/%s)>" % (self.id, self.iso2, self.name) File "C:\Python27\lib\site-packages\sqlalchemy\orm\attributes.py", line 251, in __get__ return self.impl.get(instance_state(instance), dict_) File "C:\Python27\lib\site-packages\sqlalchemy\orm\attributes.py", line 559, in get "correctly" % key) KeyError: "Deferred loader for attribute 'name' failed to populate correctly" The dict_ has no key 'name' :-( Do you have any suggestions how to fix this? > Though here, there's a less intrusive way to get this behavior if ORM > level is all you need which is by using hybrids: > > class Country(Base): > # ... > > name_de = Column(String) > name_en = Column(String) > > @hybrid_property > def name(self): > return getattr(self, "name_%s" % GLOBAL_LANGUAGE) > > @name.setter > def name(self, value): > setattr(self, "name_%s" % GLOBAL_LANGUAGE, value) > > the above can be generalized: > > def make_hybrid(name): > @hybrid_property > def name_attr(self): > return getattr(self, "%s_%s" % (name, GLOBAL_LANGUAGE)) > > @name_attr.setter > def name_attr(self, value): > setattr(self, "%s_%s" % (name, GLOBAL_LANGUAGE), value) > > name_attr.__name__ = name > return name_attr > > This will work but this will load all columns from the db... -- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To view this discussion on the web visit https://groups.google.com/d/msg/sqlalchemy/-/G2iOrjWIArAJ. To post to this group, send email to sqlalchemy@googlegroups.com. To unsubscribe from this group, send email to sqlalchemy+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en.