Match default language by id, not identifier.

This commit is contained in:
Eevee 2011-03-29 20:15:41 -07:00
parent 1268a76832
commit 825d40c51e
2 changed files with 45 additions and 16 deletions

View file

@ -2,7 +2,7 @@ from sqlalchemy import MetaData, Table, engine_from_config, orm
from ..defaults import get_default_db_uri from ..defaults import get_default_db_uri
from .tables import metadata from .tables import metadata
from .multilang import MultilangSession from .multilang import MultilangSession, MultilangScopedSession
def connect(uri=None, session_args={}, engine_args={}, engine_prefix=''): def connect(uri=None, session_args={}, engine_args={}, engine_prefix=''):
@ -42,6 +42,6 @@ def connect(uri=None, session_args={}, engine_args={}, engine_prefix=''):
all_session_args = dict(autoflush=True, autocommit=False, bind=engine) all_session_args = dict(autoflush=True, autocommit=False, bind=engine)
all_session_args.update(session_args) all_session_args.update(session_args)
sm = orm.sessionmaker(class_=MultilangSession, **all_session_args) sm = orm.sessionmaker(class_=MultilangSession, **all_session_args)
session = orm.scoped_session(sm) session = MultilangScopedSession(sm)
return session return session

View file

@ -3,6 +3,7 @@ from functools import partial
from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import aliased, compile_mappers, mapper, relationship, synonym from sqlalchemy.orm import aliased, compile_mappers, mapper, relationship, synonym
from sqlalchemy.orm.collections import attribute_mapped_collection from sqlalchemy.orm.collections import attribute_mapped_collection
from sqlalchemy.orm.scoping import ScopedSession
from sqlalchemy.orm.session import Session, object_session from sqlalchemy.orm.session import Session, object_session
from sqlalchemy.schema import Column, ForeignKey, Table from sqlalchemy.schema import Column, ForeignKey, Table
from sqlalchemy.sql.expression import and_, bindparam, select from sqlalchemy.sql.expression import and_, bindparam, select
@ -96,7 +97,6 @@ def create_translation_table(_table_name, foreign_class, relation_name,
'foreign_id': synonym(foreign_key_name), 'foreign_id': synonym(foreign_key_name),
'local_language': relationship(language_class, 'local_language': relationship(language_class,
primaryjoin=table.c.local_language_id == language_class.id, primaryjoin=table.c.local_language_id == language_class.id,
lazy='joined',
innerjoin=True), innerjoin=True),
}) })
@ -110,21 +110,19 @@ def create_translation_table(_table_name, foreign_class, relation_name,
)) ))
# Foo.bars_local # Foo.bars_local
# This is a bit clever; it uses bindparam() to make the join clause # This is a bit clever; it uses bindparam() to make the join clause
# modifiable on the fly. db sessions know the current language identifier # modifiable on the fly. db sessions know the current language and
# populates the bindparam. The manual alias and join are (a) to make the # populate the bindparam.
# condition nice (sqla prefers an EXISTS) and to make the columns play nice # The 'dummy' value is to trick SQLA; without it, SQLA thinks this
# when foreign_class == language_class. # bindparam is just its own auto-generated clause and everything gets
# fucked up.
local_relation_name = relation_name + '_local' local_relation_name = relation_name + '_local'
language_class_a = aliased(language_class)
setattr(foreign_class, local_relation_name, relationship(Translations, setattr(foreign_class, local_relation_name, relationship(Translations,
primaryjoin=and_( primaryjoin=and_(
foreign_class.id == Translations.foreign_id, Translations.foreign_id == foreign_class.id,
Translations.local_language_id == select( Translations.local_language_id == bindparam('_default_language_id',
[language_class_a.id], value='dummy', type_=Integer, required=True),
language_class_a.identifier ==
bindparam('_default_language', required=True),
),
), ),
foreign_keys=[Translations.foreign_id, Translations.local_language_id],
uselist=False, uselist=False,
#innerjoin=True, #innerjoin=True,
lazy=relation_lazy, lazy=relation_lazy,
@ -152,11 +150,42 @@ def create_translation_table(_table_name, foreign_class, relation_name,
class MultilangSession(Session): class MultilangSession(Session):
"""A tiny Session subclass that adds support for a default language.""" """A tiny Session subclass that adds support for a default language."""
default_language = 'en' _default_language_id = 9 # English. XXX magic constant
@property
def default_language(self):
# XXX need to get the right mapped class for this to work
raise NotImplementedError
@default_language.setter
def default_language(self, new):
self._default_language_id = new#.id
@default_language.deleter
def default_language(self):
try:
del self._default_language_id
except AttributeError:
pass
def execute(self, clause, params=None, *args, **kwargs): def execute(self, clause, params=None, *args, **kwargs):
if not params: if not params:
params = {} params = {}
params.setdefault('_default_language', self.default_language) params.setdefault('_default_language_id', self._default_language_id)
return super(MultilangSession, self).execute( return super(MultilangSession, self).execute(
clause, params, *args, **kwargs) clause, params, *args, **kwargs)
class MultilangScopedSession(ScopedSession):
"""Dispatches language selection to the attached Session."""
@property
def default_language(self):
return self.registry().default_language
@default_language.setter
def default_language(self, new):
self.registry().default_language = new
def remove(self):
del self.registry().default_language
super(MultilangScopedSession, self).remove()