Skip to content

Commit 543d49f

Browse files
committed
Allow overriding language_code field in translation models. Closes KristianOellegaard#332.
1 parent 491fb6e commit 543d49f

File tree

4 files changed

+33
-10
lines changed

4 files changed

+33
-10
lines changed

docs/public/models.rst

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ regular Django, with the following additional features:
2323
- Translatable fields can be used in the model options. For options that take
2424
groupings of fields (``unique_together`` and ``index_together``), each grouping
2525
may have either translatable or non-translatable fields, but not both.
26-
- Special field ``language_code`` may be used for defining ``unique_together``
27-
constraints that are only unique per language.
26+
- Special field ``language_code`` is automatically created by hvad, and may be used
27+
for defining ``unique_together`` constraints that are only unique per language.
2828

2929
A full example of a model with translations::
3030

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

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

4852
***********************
4953
New and Changed Methods

docs/public/release_notes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ New features:
2727
See the section about
2828
:ref:`overriding the default queryset <override-default-queryset>`
2929
for advantages and caveats of doing so.
30+
- Field declaration for internal ``language_code`` attribute can be overriden.
31+
:issue:`332`.
3032

3133
Compatibility warnings:
3234

@@ -53,6 +55,9 @@ Fixes:
5355

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

5762
*****************************
5863
1.7.0 - current release

hvad/models.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,18 @@
1818

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

21+
forbidden_translated_fields = frozenset({'Meta', 'objects', 'master', 'master_id'})
22+
2123
#===============================================================================
2224

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

2628
def __init__(self, meta=None, base_class=None, **fields):
29+
forbidden = forbidden_translated_fields.intersection(fields)
30+
if forbidden:
31+
raise ImproperlyConfigured(
32+
'Invalid translated field: %s' % ', '.join(sorted(forbidden)))
2733
self.meta = meta or {}
2834
self.base_class = base_class
2935
self.fields = fields
@@ -76,14 +82,12 @@ def create_translations_model(self, model, related_name):
7682
})
7783

7884
if not model._meta.abstract:
79-
attrs.update({
80-
# If this class is abstract, we must not contribute management fields
81-
'objects': TranslationsModelManager(),
82-
'language_code': models.CharField(max_length=15, db_index=True),
83-
# Nullable so we can prevent cascade deletion
84-
'master': models.ForeignKey(model, related_name=related_name, editable=False,
85-
on_delete=models.CASCADE),
86-
})
85+
# If this class is abstract, we must not contribute management fields
86+
attrs['objects'] = TranslationsModelManager()
87+
attrs['master'] = models.ForeignKey(model, related_name=related_name,
88+
editable=False, on_delete=models.CASCADE)
89+
if 'language_code' not in attrs: # allow overriding
90+
attrs['language_code'] = models.CharField(max_length=15, db_index=True)
8791

8892
# Create the new model
8993
if self.base_class:

hvad/tests/basic.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,16 @@ def test_manager_properties(self):
262262
manager = Normal.objects
263263
self.assertEqual(manager.translations_model, Normal._meta.translations_model)
264264

265+
def test_language_code_override(self):
266+
class LanguageCodeOverrideModel(TranslatableModel):
267+
translations = TranslatedFields(
268+
tfield=models.CharField(max_length=250),
269+
language_code=models.UUIDField(editable=False, db_index=True),
270+
)
271+
tmodel = LanguageCodeOverrideModel._meta.translations_model
272+
self.assertIsInstance(tmodel._meta.get_field('language_code'), models.UUIDField)
273+
274+
265275
class OptionsTest(HvadTestCase):
266276
def test_options(self):
267277
self.assertEqual(Normal._meta.translations_model.__name__, 'NormalTranslation')

0 commit comments

Comments
 (0)