Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases now! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Django 3 Web Development Cookbook

You're reading from   Django 3 Web Development Cookbook Actionable solutions to common problems in Python web development

Arrow left icon
Product type Paperback
Published in Mar 2020
Publisher Packt
ISBN-13 9781838987428
Length 608 pages
Edition 4th Edition
Languages
Tools
Arrow right icon
Authors (2):
Arrow left icon
Jake Kronika Jake Kronika
Author Profile Icon Jake Kronika
Jake Kronika
Aidas Bendoraitis Aidas Bendoraitis
Author Profile Icon Aidas Bendoraitis
Aidas Bendoraitis
Arrow right icon
View More author details
Toc

Table of Contents (15) Chapters Close

Preface 1. Getting Started with Django 3.0 2. Models and Database Structure FREE CHAPTER 3. Forms and Views 4. Templates and JavaScript 5. Custom Template Filters and Tags 6. Model Administration 7. Security and Performance 8. Hierarchical Structures 9. Importing and Exporting Data 10. Bells and Whistles 11. Testing 12. Deployment 13. Maintenance 14. Other Books You May Enjoy

Working with model translation tables

The second approach to handling multilingual content in the database involves using model translation tables for each multilingual model.

The features of this approach are as follows:

  • You can use contributed administration to edit translations as inlines.
  • After changing the amount of languages in the settings, no migrations or other further actions are necessary.
  • You can effortlessly show the translation of the current language in the template, but it would be more difficult to show several translations in specific languages on the same page.
  • You have to know and use a specific pattern described in this recipe for creating model translations.
  • It's not that simple to use this approach for database queries, but, as you will see, it's still possible.

Getting ready

Once again, we will start with the myprojects.apps.core app.

How to do it...

Execute the following steps to prepare for multilingual models:

  1. In the core app, create model_fields.py with the following content:
# myproject/apps/core/model_fields.py
from
django.conf import settings
from django.utils.translation import get_language
from django.utils import translation

class TranslatedField(object):
def __init__(self, field_name):
self.field_name = field_name

def __get__(self, instance, owner):
lang_code = translation.get_language()
if lang_code == settings.LANGUAGE_CODE:
# The fields of the default language are in the main
model
return getattr(instance, self.field_name)
else:
# The fields of the other languages are in the
translation
# model, but falls back to the main model
translations = instance.translations.filter(
language=lang_code,
).first() or instance
return getattr(translations, self.field_name)
  1. Add the admin.py file to the core app with the following content:
# myproject/apps/core/admin.py
from
django import forms
from django.conf import settings
from django.utils.translation import gettext_lazy as _

class LanguageChoicesForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
LANGUAGES_EXCEPT_THE_DEFAULT = [
(lang_code, lang_name)
for lang_code, lang_name in settings.LANGUAGES
if lang_code != settings.LANGUAGE_CODE
]
super().__init__(*args, **kwargs)
self.fields["language"] = forms.ChoiceField(
label=_("Language"),
choices=LANGUAGES_EXCEPT_THE_DEFAULT,
required=True,
)

Now let's implement the multilingual models:

  1. First, set multiple languages in the settings for your project. Let's say, our website will support all official languages of European Union with English being the default language:
# myproject/settings/_base.py
LANGUAGE_CODE = "en"

# All official languages of European Union
LANGUAGES = [
("bg", "Bulgarian"), ("hr", "Croatian"),
("cs", "Czech"), ("da", "Danish"),
("nl", "Dutch"), ("en", "English"),
("et", "Estonian"), ("fi", "Finnish"),
("fr", "French"), ("de", "German"),
("el", "Greek"), ("hu", "Hungarian"),
("ga", "Irish"), ("it", "Italian"),
("lv", "Latvian"), ("lt", "Lithuanian"),
("mt", "Maltese"), ("pl", "Polish"),
("pt", "Portuguese"), ("ro", "Romanian"),
("sk", "Slovak"), ("sl", "Slovene"),
("es", "Spanish"), ("sv", "Swedish"),
]
  1. Then, let's create the Idea and IdeaTranslations models:
# myproject/apps/ideas/models.py
from
django.db import models
from django.conf import settings
from django.utils.translation import gettext_lazy as _

from myproject.apps.core.model_fields import TranslatedField


class Idea(models.Model):
title = models.CharField(
_("Title"),
max_length=200,
)
content = models.TextField(
_("Content"),
)
translated_title = TranslatedField("title")
translated_content = TranslatedField("content")

class Meta:
verbose_name = _("Idea")
verbose_name_plural = _("Ideas")

def __str__(self):
return self.title


class IdeaTranslations(models.Model):
idea = models.ForeignKey(
Idea,
verbose_name=_("Idea"),
on_delete=models.CASCADE,
related_name="translations",
)
language = models.CharField(_("Language"), max_length=7)

title = models.CharField(
_("Title"),
max_length=200,
)
content = models.TextField(
_("Content"),
)

class Meta:
verbose_name = _("Idea Translations")
verbose_name_plural = _("Idea Translations")
ordering = ["language"]
unique_together = [["idea", "language"]]

def __str__(self):
return self.title
  1. Last, create the admin.py for the ideas app as follows:
# myproject/apps/ideas/admin.py
from
django.contrib import admin
from django.utils.translation import gettext_lazy as _

from myproject.apps.core.admin import LanguageChoicesForm

from .models import Idea, IdeaTranslations


class IdeaTranslationsForm(LanguageChoicesForm):
class Meta:
model = IdeaTranslations
fields = "__all__"


class IdeaTranslationsInline(admin.StackedInline):
form = IdeaTranslationsForm
model = IdeaTranslations
extra = 0


@admin.register(Idea)
class IdeaAdmin(admin.ModelAdmin):
inlines = [IdeaTranslationsInline]

fieldsets = [
(_("Title and Content"), {
"fields": ["title", "content"]
}),
]

How it works...

We keep the language-specific fields of the default language in the Idea model itself. The translations for each language are in the IdeaTranslations model, which will be listed in the administration as an inline translation. IdeaTranslations don't have the language choices at the model for a reason – we don't want to create migrations every time a new language is added or some language is removed. Instead, the language choices are set in the administration form, also making sure that the default language is skipped or not available for selection in the list. The language choices are restricted using the LanguageChoicesForm class.

To get a specific field in the current language, you would use the fields defined as TranslatedField. In the template, that would look like the following:

<h1>{{ idea.translated_title }}</h1>
<div>{{ idea.translated_content|urlize|linebreaks }}</div>

To order items by a translated title in a specific language, you would use the annotate() method as follows:

>>> from django.conf import settings
>>> from django.db import models
>>> lang_code = input("Enter language code: ")

>>> if lang_code == settings.LANGUAGE_CODE:
... qs = Idea.objects.annotate(
... title_translation=models.F("title"),
... content_translation=models.F("content"),
... )
... else:
... qs = Idea.objects.filter(
... translations__language=lang_code,
... ).annotate(
... title_translation=models.F("translations__title"),
... content_translation=models.F("translations__content"),
... )

>>> qs = qs.order_by("title_translation")

>>> for idea in qs:
... print(idea.title_translation)

In this example, we prompt for a language code in the Django shell. If the language is the default one, we store the title and content as the title_translation and the content_translation from the Idea model. If there is another language chosen, we read the title and content as title_translation and content_translation from the IdeaTranslations model with the chosen language.

Afterward, we can filter or order QuerySet by title_translation or content_translation.

See also

  • The Handling multilingual fields recipe
  • Chapter 6, Model Administration
You have been reading a chapter from
Django 3 Web Development Cookbook - Fourth Edition
Published in: Mar 2020
Publisher: Packt
ISBN-13: 9781838987428
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €18.99/month. Cancel anytime