wagtail-modeltranslation/docs/Registering Models.rst

214 lines
8.9 KiB
ReStructuredText

.. _Registering Models:
Registering models for translation
==================================
``Modeltranslation`` can translate model fields of any model class.
In wagtail-modeltranslation a **TranslationMixin** is used with the Page model:
.. code-block::console
from wagtail_modeltranslation.models import TranslationMixin
class FooModel(TranslationMixin, Page):
body = StreamField(...)
Registering models and their fields used for translation requires the following steps:
1. Create **translation.py** in your app directory.
2. Define the models you want to use, import wagtail-modeltranslation's **TranslationOptions** and the **register** decorator
3. Create a translation option class for every model you want to translate and precede the class with the **@register** decorator.
The wagtail-modeltranslation application reads the **translation.py** file in your app directory thereby triggering the registration
of the translation options found in the file.
A translation option is a class that declares which model fields are needed for translation. The class must derive from
**wagtail_modeltranslation.translator.TranslationOptions** and it must provide a **field** attribute storing the list of
field names. The option class must be registered with the **wagtail_modeltranslation.decorators.register** instance.
To illustrate this let's have a look at a simple example using a **Foo** model. The example only contains an **introduction**
and a **body** field.
Instead of a **Foo** model, this could be any Wagtail model class:
.. code-block:: console
from .models import Foo
from wagtail_modeltranslation.translator import TranslationOptions
from wagtail_modeltranslation.decorators import register
@register(Foo)
class FooTR(TranslationOptions):
fields = (
'introduction',
'body',
)
In the above example, the **introduction** and **body** language fields will be be added for each language defined in
LANGUAGES in the settings file ,**base.py**, when the database is updated with **./manage.py makemigrations** and
**./manage.py migrate.**
At this point you are mostly done and the model classes registered for translation will have been added some auto-magical
fields. The next section explains how things are working under the hood.
Changes automatically applied to the model class
----------------------------------------------------
After registering the **Foo** model for translation a SQL dump of the Foo app will look like this:
.. code-block:: console
$ ./manage.py sqlall news
BEGIN;
CREATE TABLE `news_Foo` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`introduction` varchar(255) NOT NULL,
`introduction_pt` varchar(255) NULL,
`introduction_es` varchar(255) NULL,
`introduction_fr` varchar(255) NULL,
`body` varchar(255) NOT NULL,
`body_pt` varchar(255) NULL,
`body_es` varchar(255) NULL,
`body_fr` varchar(255) NULL,
)
;
CREATE INDEX `news_Foo_page_id` ON `news_Foo` (`page_id`);
COMMIT;
Note the ``introduction_pt``, ``introduction_es``, ``introduction_fr``, ``body_pt``, ``body_es`` and ``body_fr`` fields
which are not declared in the original ``Foo`` model class have been added by the modeltranslation app. These are called
*translation fields*. There will be one for every language in your project's ``settings.py``.
The name of these additional fields is build using the original name of the translated field and appending one of the
language identifiers found in the ``settings.LANGUAGES``.
As these fields are added to the registered model class as fully valid Django model fields, they will appear in the db schema
for the model although it has not been specified on the model explicitly.
.. _register-precautions:
Precautions regarding registration approach
-------------------------------------------
Be aware that registration approach (as opposed to base-class approach) to models translation has a few caveats, though
(despite many pros).
First important thing to note is the fact that translatable models are being patched - that means their fields list is not
final until the modeltranslation code executes. In normal circumstances it shouldn't affect anything - as long as
``models.py`` contain only models' related code.
For example: consider a project where a ``ModelForm`` is declared in ``models.py`` just after its model. When the file is
executed, the form gets prepared - but it will be frozen with old fields list (without translation fields). That's because the
``ModelForm`` will be created before modeltranslation would add new fields to the model (``ModelForm`` gathers fields info at
class creation time, not instantiation time). Proper solution is to define the form in ``forms.py``, which wouldn't be imported
alongside with **models.py** (and rather imported from views file or urlconf).
Generally, for seamless integration with modeltranslation (and as sensible design anyway), the models.py`` should contain
only bare models and model related logic.
.. _db-fields:
Committing fields to database
-----------------------------
.. _migrations:
Modeltranslation supports the migration system introduced by Django 1.7. Besides the normal workflow as described in Django's
`Migration Docs <https://docs.djangoproject.com/en/1.8/topics/migrations/>`__, you should do a migration whenever one of the following changes have been made to your project:
- Added or removed a language through ``settings.LANGUAGES`` or ``settings.MODELTRANSLATION LANGUAGES``.
- Registered or unregistered a field through ``TranslationOptions``.
It doesn't matter if you are starting a fresh project or change an existing one, it's always:
1. ``python manage.py makemigration`` to create a new migration with
the added or removed fields.
2. ``python manage.py migrate`` to apply the changes.
.. _required_langs:
Required fields
---------------
By default, all translation fields are optional (not required). This can be changed using a special attribute on
``TranslationOptions``::
class NewsTranslationOptions(TranslationOptions):
fields = ('introduction', 'body',)
required_languages = ('pt', 'es')
It's quite self-explanatory: for Portuguese and Spanish, the ``introduction`` and ``body`` translation fields are required. For other
languages, they are optional.
A more fine-grained control is available::
class NewsTranslationOptions(TranslationOptions):
fields = ('introduction', 'body',)
required_languages = {'pt': ('introduction', 'body'), 'default': ('introduction',)}
For Portuguese, all fields (both ``introduction`` and ``body``) are required; for all other languages, only
``introduction`` is required. The ``default`` is optional.
.. note::
Requirement is enforced by ``blank=False``. Please remember that it will trigger validation only
in modelforms and admin (as always in Django). Manual model validation can be performed via
the ``full_clean()`` model method.
The required fields are still ``null=True``, though.
.. _supported_field_matrix:
Matrix of supported fields
--------------------------
While the main purpose of modeltranslation is to translate text-like fields, translating other fields can be useful in
several situations. The table lists all model fields available in Django and Wagtail and gives an overview about their
current support status.
=============================== ===============
Model Field Implemented
=============================== ===============
**AutoField** |n|
**BigIntegerField** |i|
**BooleanField** |y|
**CharField** |y|
**CommaSeparatedIntegerField** |y|
**DateField** |y|
**DateTimeField** |y|
**DecimalField** |y|
**EmailField** |i|
**FileField** |y|
**FilePathField** |i|
**FloatField** |y|
**ImageField** |y|
**IntegerField** |y|
**IPAddressField** |y|
**GenericIPAddressField** |y|
**NullBooleanField** |y|
**PositiveIntegerField** |i|
**PositiveSmallIntegerField** |i|
**SlugField** |i|
**SmallIntegerField** |i|
**StreamField** |y|
**TextField** |y|
**TimeField** |y|
**URLField** |i|
**ForeignKey** |y|
**OneToOneField** |y|
**ManyToManyField** |n|
=============================== ===============
.. |y| replace:: Yes
.. |i| replace:: Yes\*
.. |n| replace:: No
.. |u| replace:: ?
\* Implicitly supported (as subclass of a supported field)