Skip to content

Commit

Permalink
Make relationship and fk args customizable
Browse files Browse the repository at this point in the history
* Add configuration option for relationship args
* Add foreign_key_args parameter to translation_base function
  • Loading branch information
kvesteri committed Mar 6, 2016
1 parent c666322 commit 450e849
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 17 deletions.
7 changes: 7 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ Changelog
Here you can see the full list of changes between each SQLAlchemy-i18n release.


1.0.2 (2016-03-06)
^^^^^^^^^^^^^^^^^^

- Added ``translations_relationship_args`` configuration option
- Added ``foreign_key_args`` parameter to ``translations_base`` function


1.0.1 (2014-10-21)
^^^^^^^^^^^^^^^^^^

Expand Down
28 changes: 28 additions & 0 deletions docs/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,31 @@ Other options
* fallback_locale

The locale which will be used as a fallback for translation hybrid properties that return None or empty string.

* translations_relationship_args

Dictionary of arguments passed as defaults for automatically created translations relationship.::


class Article(Base):
__tablename__ = 'article'
__translatable__ = {
'locales': [u'en', u'fi'],
'translations_relationship_args': {
'passive_deletes': False
}
}

id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)

author = sa.Column(sa.Unicode(255))

def get_locale(self):
return 'en'


class ArticleTranslation(translation_base(Article)):
__tablename__ = 'article_translation'

name = sa.Column(sa.Unicode(255))
content = sa.Column(sa.UnicodeText)
2 changes: 1 addition & 1 deletion sqlalchemy_i18n/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
)


__version__ = '1.0.1'
__version__ = '1.0.2'


def make_translatable(
Expand Down
41 changes: 29 additions & 12 deletions sqlalchemy_i18n/builders.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from copy import copy

import sqlalchemy as sa
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm.collections import attribute_mapped_collection
Expand Down Expand Up @@ -95,7 +97,8 @@ def __call__(self):


class RelationshipBuilder(object):
def __init__(self, translation_cls):
def __init__(self, manager, translation_cls):
self.manager = manager
self.translation_cls = translation_cls
self.parent_cls = self.translation_cls.__parent_class__

Expand Down Expand Up @@ -180,21 +183,35 @@ def assign_translations(self):
"""
mapper = sa.orm.class_mapper(self.parent_cls)
if not mapper.has_property('_translations'):
foreign_keys = [
getattr(self.translation_cls, column_key)
for column_key in get_primary_keys(self.parent_cls).keys()
]

mapper.add_property('_translations', sa.orm.relationship(
self.translation_cls,
primaryjoin=sa.and_(*self.primary_key_conditions),
foreign_keys=foreign_keys,
collection_class=attribute_mapped_collection('locale'),
comparator_factory=TranslationComparator,
cascade='all, delete-orphan',
passive_deletes=True,
**self.get_translations_relationship_args()
))

def get_translations_relationship_args(self):
foreign_keys = [
getattr(self.translation_cls, column_key)
for column_key in get_primary_keys(self.parent_cls).keys()
]

relationship_args = copy(
self.manager.option(
self.parent_cls,
'translations_relationship_args'
)
)
defaults = dict(
primaryjoin=sa.and_(*self.primary_key_conditions),
foreign_keys=foreign_keys,
collection_class=attribute_mapped_collection('locale'),
comparator_factory=TranslationComparator,
cascade='all, delete-orphan',
passive_deletes=True,
)
for key, value in defaults.items():
relationship_args.setdefault(key, value)
return relationship_args

def assign_translation_parent(self):
mapper = sa.orm.class_mapper(self.translation_cls)
if not mapper.has_property('translation_parent'):
Expand Down
17 changes: 13 additions & 4 deletions sqlalchemy_i18n/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,18 @@ class BaseTranslationMixin(object):
pass


def translation_base(parent_cls, base_class_factory=None):
def translation_base(
parent_cls,
base_class_factory=None,
foreign_key_args=None
):
if base_class_factory is None:
base_class_factory = get_declarative_base

if foreign_key_args is None:
foreign_key_args = {}
foreign_key_args.setdefault('ondelete', 'CASCADE')

class TranslationMixin(
base_class_factory(parent_cls),
BaseTranslationMixin
Expand All @@ -37,7 +45,7 @@ def __table_args__(cls):
'%s.%s' % (parent_cls.__tablename__, name)
for name in names
],
ondelete='CASCADE'
**foreign_key_args
),
)

Expand All @@ -64,7 +72,8 @@ def __init__(self):
'locales': [],
'auto_create_locales': True,
'fallback_locale': 'en',
'exclude_hybrid_properties': []
'exclude_hybrid_properties': [],
'translations_relationship_args': {}
}

def instrument_translation_classes(self, mapper, cls):
Expand Down Expand Up @@ -96,7 +105,7 @@ def configure_translatable_classes(self):
parent_cls.__translatable__['class'] = cls

HybridPropertyBuilder(self, cls)()
RelationshipBuilder(cls)()
RelationshipBuilder(self, cls)()

self.pending_classes = []

Expand Down
45 changes: 45 additions & 0 deletions tests/test_configuration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-

import sqlalchemy as sa

from sqlalchemy_i18n import Translatable, translation_base
from tests import DeclarativeTestCase


class TestTranslationBaseCustomization(DeclarativeTestCase):
def create_models(self):
class Article(self.Model, Translatable):
__tablename__ = 'article'
__translatable__ = {
'locales': ['fi', 'en'],
'auto_create_locales': True,
'translations_relationship_args': {
'collection_class': list,
}
}

id = sa.Column(sa.Integer, primary_key=True)

locale = 'en'

TranslationBase = translation_base(
Article,
foreign_key_args={'onupdate': 'SET NULL'}
)

class ArticleTranslation(TranslationBase):
__tablename__ = 'article_translation'

name = sa.Column(sa.Unicode(255))

self.Article = Article
self.ArticleTranslation = ArticleTranslation

def test_customize_foreign_key_args(self):
assert (
self.ArticleTranslation.__table__.foreign_keys.pop().onupdate ==
'SET NULL'
)

def test_customize_relationships_args(self):
assert self.Article._translations.property.collection_class == list

0 comments on commit 450e849

Please sign in to comment.