Skip to content

Commit

Permalink
Allow overriding language_code field in translation models. Closes #332.
Browse files Browse the repository at this point in the history
  • Loading branch information
spectras committed Mar 2, 2017
1 parent 491fb6e commit 543d49f
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 10 deletions.
8 changes: 6 additions & 2 deletions docs/public/models.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ regular Django, with the following additional features:
- Translatable fields can be used in the model options. For options that take
groupings of fields (``unique_together`` and ``index_together``), each grouping
may have either translatable or non-translatable fields, but not both.
- Special field ``language_code`` may be used for defining ``unique_together``
constraints that are only unique per language.
- Special field ``language_code`` is automatically created by hvad, and may be used
for defining ``unique_together`` constraints that are only unique per language.

A full example of a model with translations::

Expand All @@ -44,6 +44,10 @@ A full example of a model with translations::

.. note:: The :djterm:`Meta <meta-options>` class of the model may not use the
translatable fields in :attr:`~django.db.models.Options.order_with_respect_to`.
.. note:: TranslatedFields cannot contain a field named ``master``, as this name
is reserved by hvad to refer to the :term:`Shared Model`. Also, special
field ``language_code`` can be overriden in order to set it to be a
different type of field, or change its options.

***********************
New and Changed Methods
Expand Down
5 changes: 5 additions & 0 deletions docs/public/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ New features:
See the section about
:ref:`overriding the default queryset <override-default-queryset>`
for advantages and caveats of doing so.
- Field declaration for internal ``language_code`` attribute can be overriden.
:issue:`332`.

Compatibility warnings:

Expand All @@ -53,6 +55,9 @@ Fixes:

- Increase speed of translated attribute access by ~30%, by avoiding a method call
when a translation is loaded.
- Attempting to use a reserved name for a translated field now raises an
:exc:`~django.core.exceptions.ImproperlyConfigured` exception instead of silently
ignoring the field.

*****************************
1.7.0 - current release
Expand Down
20 changes: 12 additions & 8 deletions hvad/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,18 @@

__all__ = ('TranslatableModel', 'TranslatedFields', 'NoTranslation')

forbidden_translated_fields = frozenset({'Meta', 'objects', 'master', 'master_id'})

#===============================================================================

class TranslatedFields(object):
""" Wrapper class to define translated fields on a model. """

def __init__(self, meta=None, base_class=None, **fields):
forbidden = forbidden_translated_fields.intersection(fields)
if forbidden:
raise ImproperlyConfigured(
'Invalid translated field: %s' % ', '.join(sorted(forbidden)))
self.meta = meta or {}
self.base_class = base_class
self.fields = fields
Expand Down Expand Up @@ -76,14 +82,12 @@ def create_translations_model(self, model, related_name):
})

if not model._meta.abstract:
attrs.update({
# If this class is abstract, we must not contribute management fields
'objects': TranslationsModelManager(),
'language_code': models.CharField(max_length=15, db_index=True),
# Nullable so we can prevent cascade deletion
'master': models.ForeignKey(model, related_name=related_name, editable=False,
on_delete=models.CASCADE),
})
# If this class is abstract, we must not contribute management fields
attrs['objects'] = TranslationsModelManager()
attrs['master'] = models.ForeignKey(model, related_name=related_name,
editable=False, on_delete=models.CASCADE)
if 'language_code' not in attrs: # allow overriding
attrs['language_code'] = models.CharField(max_length=15, db_index=True)

# Create the new model
if self.base_class:
Expand Down
10 changes: 10 additions & 0 deletions hvad/tests/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,16 @@ def test_manager_properties(self):
manager = Normal.objects
self.assertEqual(manager.translations_model, Normal._meta.translations_model)

def test_language_code_override(self):
class LanguageCodeOverrideModel(TranslatableModel):
translations = TranslatedFields(
tfield=models.CharField(max_length=250),
language_code=models.UUIDField(editable=False, db_index=True),
)
tmodel = LanguageCodeOverrideModel._meta.translations_model
self.assertIsInstance(tmodel._meta.get_field('language_code'), models.UUIDField)


class OptionsTest(HvadTestCase):
def test_options(self):
self.assertEqual(Normal._meta.translations_model.__name__, 'NormalTranslation')
Expand Down

0 comments on commit 543d49f

Please sign in to comment.