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.

Reply via email to