Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support SQLAlchemy 1.4 #361

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion pokedex/db/load.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from pokedex.db.dependencies import find_dependent_tables
from pokedex.db.oracle import rewrite_long_table_names

from sqlalchemy import and_
from sqlalchemy import and_, true
from sqlalchemy.sql import exists


Expand Down Expand Up @@ -381,6 +381,7 @@ def insert_and_commit():
session.query(VGPMM).delete()

q = session.query(t.VersionGroup.id, t.PokemonMoveMethod.id)
q = q.filter(true()) # Suppress cartesian product warning
q = q.filter(exists().where(and_(
t.PokemonMove.pokemon_move_method_id == t.PokemonMoveMethod.id,
t.PokemonMove.version_group_id == t.VersionGroup.id)))
Expand Down
44 changes: 34 additions & 10 deletions pokedex/db/multilang.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@

from pokedex.db import markdown

# Decide which method to use for the default value of the parameter _default_language_id
_MULTILANG_SESSION_USE_EVENT = False
try:
from sqlalchemy.orm import SessionEvents
except ImportError:
pass
else:
if hasattr(SessionEvents, 'do_orm_execute'):
# SQLAlchemy 1.4+
from sqlalchemy import event
_MULTILANG_SESSION_USE_EVENT = True

class LocalAssociationProxy(AssociationProxy, ColumnOperators):
"""An association proxy for names in the default language

Expand Down Expand Up @@ -168,11 +180,12 @@ class Foo(Base): ...
primaryjoin=and_(
Translations.foreign_id == foreign_class.id,
Translations.local_language_id == bindparam('_default_language_id',
value='dummy', type_=Integer, required=True),
value='dummy', type_=Integer),
),
foreign_keys=[Translations.foreign_id, Translations.local_language_id],
uselist=False,
lazy=relation_lazy,
viewonly=True,
))

# Add per-column proxies to the original class
Expand Down Expand Up @@ -206,14 +219,16 @@ def creator(language, value):
# Done
return Translations

class MultilangQuery(Query):
def _execute_and_instances(self, *args, **kwargs):
# Set _default_language_id param if it hasn't been set by the time the query is executed.
# XXX This is really hacky and we should figure out a cleaner method.
if '_default_language_id' not in self._params or self._params['_default_language_id'] == 'dummy':
self._params = self._params.copy()
self._params['_default_language_id'] = self.session.default_language_id
return super(MultilangQuery, self)._execute_and_instances(*args, **kwargs)
if not _MULTILANG_SESSION_USE_EVENT:
# SQLAlchemy 1.4 no longer supports Query._execute_and_instances
class MultilangQuery(Query):
def _execute_and_instances(self, *args, **kwargs):
# Set _default_language_id param if it hasn't been set by the time the query is executed.
# XXX This is really hacky and we should figure out a cleaner method.
if '_default_language_id' not in self._params or self._params['_default_language_id'] == 'dummy':
self._params = self._params.copy()
self._params['_default_language_id'] = self.session.default_language_id
return super(MultilangQuery, self)._execute_and_instances(*args, **kwargs)

class MultilangSession(Session):
"""A tiny Session subclass that adds support for a default language.
Expand All @@ -232,10 +247,19 @@ def __init__(self, *args, **kwargs):

self.markdown_extension = markdown_extension_class(self)

kwargs.setdefault('query_cls', MultilangQuery)
if not _MULTILANG_SESSION_USE_EVENT:
kwargs.setdefault('query_cls', MultilangQuery)

super(MultilangSession, self).__init__(*args, **kwargs)

if _MULTILANG_SESSION_USE_EVENT:
@event.listens_for(MultilangSession, 'do_orm_execute')
def receive_do_orm_execute(state):
# Set _default_language_id param if it hasn't been set by the time the query is executed.
# The same hack as above, but for SQLAlchemy 1.4+
if state.is_select and state.parameters.get('_default_language_id', 'dummy') == 'dummy':
return state.invoke_statement(params={'_default_language_id': state.session.default_language_id})

class MultilangScopedSession(ScopedSession):
"""Dispatches language selection to the attached Session."""

Expand Down
27 changes: 15 additions & 12 deletions pokedex/db/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import six

from sqlalchemy import Column, ForeignKey, MetaData, PrimaryKeyConstraint, UniqueConstraint
from sqlalchemy import __version__ as sqla_version
from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.hybrid import hybrid_property
Expand All @@ -39,6 +40,12 @@

from pokedex.db import markdown, multilang

relationship = partial(relationship, viewonly=True)
if (1, 3, 17) <= tuple(int(x) for x in sqla_version.split(".")) < (1, 4):
# `sync_backref` was introduced in 1.3.17
# Since 1.4 it defaults to False if `viewonly` is True
relationship = partial(relationship, sync_backref=False)

class TableSuperclass(object):
"""Superclass for declarative tables, to give them some generic niceties
like stringification.
Expand Down Expand Up @@ -2475,7 +2482,8 @@ class VersionGroupRegion(TableBase):
Generation.versions = relationship(Version,
secondary=VersionGroup.__table__,
innerjoin=True)
Generation.main_region = relationship(Region, innerjoin=True)
Generation.main_region = relationship(Region, innerjoin=True,
backref=backref('generation', uselist=False))


GrowthRate.max_experience_obj = relationship(Experience,
Expand All @@ -2497,14 +2505,13 @@ class VersionGroupRegion(TableBase):
Item.fling_effect = relationship(ItemFlingEffect,
backref='items')
Item.machines = relationship(Machine,
order_by=Machine.version_group_id.asc())
order_by=Machine.version_group_id.asc(),
backref='item')
Item.category = relationship(ItemCategory,
innerjoin=True,
backref=backref('items', order_by=Item.identifier.asc()))
Item.pocket = association_proxy('category', 'pocket')

ItemCategory.pocket = relationship(ItemPocket, innerjoin=True)

ItemFlavorText.version_group = relationship(VersionGroup,
innerjoin=True, lazy='joined')
ItemFlavorText.language = relationship(Language,
Expand All @@ -2518,7 +2525,8 @@ class VersionGroupRegion(TableBase):

ItemPocket.categories = relationship(ItemCategory,
innerjoin=True,
order_by=ItemCategory.identifier.asc())
order_by=ItemCategory.identifier.asc(),
backref=backref('pocket', innerjoin=True))


Location.region = relationship(Region,
Expand All @@ -2542,11 +2550,6 @@ class VersionGroupRegion(TableBase):
innerjoin=True, lazy='joined')


Machine.item = relationship(Item)
Machine.version_group = relationship(VersionGroup,
innerjoin=True, lazy='joined')


Move.changelog = relationship(MoveChangelog,
order_by=MoveChangelog.changed_in_version_group_id.desc(),
backref=backref('move', innerjoin=True, lazy='joined'))
Expand Down Expand Up @@ -2886,7 +2889,6 @@ class VersionGroupRegion(TableBase):
PokemonSpeciesFlavorText.version = relationship(Version, innerjoin=True, lazy='joined')
PokemonSpeciesFlavorText.language = relationship(Language, innerjoin=True, lazy='joined')

Region.generation = relationship(Generation, uselist=False)
Region.version_group_regions = relationship(VersionGroupRegion,
order_by=VersionGroupRegion.version_group_id.asc(),
backref='region')
Expand Down Expand Up @@ -2950,7 +2952,8 @@ class VersionGroupRegion(TableBase):
backref="version_groups")
VersionGroup.machines = relationship(Machine,
innerjoin=True,
order_by=Machine.machine_number)
order_by=Machine.machine_number,
backref=backref('version_group', innerjoin=True, lazy='joined'))


VersionGroupPokemonMoveMethod.version_group = relationship(VersionGroup,
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
'pokedex': ['data/csv/*.csv']
},
install_requires=[
'SQLAlchemy>=1.0,<1.4',
'SQLAlchemy>=1.0,<2.0',
'whoosh>=2.5,<2.7',
'markdown>=2.4.1,<=2.6.11',
'construct==2.5.3',
Expand Down