Integration with django-polymorphic¶
When you have to combine TranslatableModel
with PolymorphicModel
you
have to make sure the model managers of both classes are combined too.
This can be done by either overwriting default_manager
or by extending the Manager
and QuerySet
class.
Combining TranslatableModel
with PolymorphicModel
¶
Say we have a base Product
with two concrete products, a Book
with two translatable fields
name
and slug
, and a Pen
with one translatable field identifier
. Then the following
pattern works for a polymorphic Django model:
from django.db import models
from django.utils.encoding import force_text
from parler.models import TranslatableModel, TranslatedFields
from parler.managers import TranslatableManager
from polymorphic import PolymorphicModel
from .managers import BookManager
class Product(PolymorphicModel):
# The shared base model. Either place translated fields here,
# or place them at the subclasses (see note below).
code = models.CharField(blank=False, default='', max_length=16)
price = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
class Book(Product, TranslatableModel):
# Solution 1: use a custom manager that combines both.
objects = BookManager()
translations = TranslatedFields(
name=models.CharField(blank=False, default='', max_length=128),
slug=models.SlugField(blank=False, default='', max_length=128)
)
def __str__(self):
return force_text(self.code)
class Pen(Product, TranslatableModel):
# Solution 2: override the default manager.
default_manager = TranslatableManager()
translations = TranslatedFields(
identifier=models.CharField(blank=False, default='', max_length=255)
)
def __str__(self):
return force_text(self.identifier)
The only precaution one must take, is to override the default manager in each of the classes containing translatable fields. This is shown in the example above.
As of django-parler 1.2 it’s possible to have translations on both the base and derived models.
Make sure that the field name (in this case translations
) differs between both models,
as that name is used as related_name
for the translated fields model
Combining managers¶
The managers can be combined by inheriting them, and specifying
the queryset_class
attribute
with both django-parler and django-polymorphic use.
from parler.managers import TranslatableManager, TranslatableQuerySet
from polymorphic import PolymorphicManager
from polymorphic.query import PolymorphicQuerySet
class BookQuerySet(TranslatableQuerySet, PolymorphicQuerySet):
pass
class BookManager(PolymorphicManager, TranslatableManager):
queryset_class = BookQuerySet
Assign the manager to the model objects
attribute.
Implementing the admin¶
It is perfectly possible to to register individual polymorphic models in the Django admin interface. However, to use these models in a single cohesive interface, some extra base classes are available.
This admin interface adds translatable fields to a polymorphic model:
from django.contrib import admin
from parler.admin import TranslatableAdmin, TranslatableModelForm
from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin
from .models import BaseProduct, Book, Pen
class BookAdmin(TranslatableAdmin, PolymorphicChildModelAdmin):
base_form = TranslatableModelForm
base_model = BaseProduct
base_fields = ('code', 'price', 'name', 'slug')
class PenAdmin(TranslatableAdmin, PolymorphicChildModelAdmin):
base_form = TranslatableModelForm
base_model = BaseProduct
base_fields = ('code', 'price', 'identifier',)
class BaseProductAdmin(PolymorphicParentModelAdmin):
base_model = BaseProduct
child_models = ((Book, BookAdmin), (Pen, PenAdmin),)
list_display = ('code', 'price',)
admin.site.register(BaseProduct, BaseProductAdmin)