{"id":8108,"library":"django-parler","title":"Django Parler","description":"Django Parler (current version 2.3) provides simple Django model translations without nasty hacks, featuring nice admin integration. It enables storing multiple language versions of model fields in a separate table. The library is actively maintained with updates for Django compatibility and new features regularly released.","status":"active","version":"2.3","language":"en","source_language":"en","source_url":"https://github.com/edoburu/django-parler","tags":["django","i18n","l10n","translations","models","admin","internationalization"],"install":[{"cmd":"pip install django-parler","lang":"bash","label":"Install latest stable version"}],"dependencies":[],"imports":[{"symbol":"TranslatableModel","correct":"from parler.models import TranslatableModel"},{"symbol":"TranslatedFields","correct":"from parler.models import TranslatedFields"},{"symbol":"TranslatableAdmin","correct":"from parler.admin import TranslatableAdmin"},{"note":"Used for explicitly declaring individual translated fields, especially when needing custom options like 'any_language'.","symbol":"TranslatedField","correct":"from parler.fields import TranslatedField"}],"quickstart":{"code":"# settings.py\nINSTALLED_APPS = [\n    # ...\n    'parler',\n    # Your app\n    'myapp',\n]\n\nLANGUAGE_CODE = 'en'\nLANGUAGES = (\n    ('en', 'English'),\n    ('fr', 'French'),\n    ('es', 'Spanish'),\n)\n\nPARLER_LANGUAGES = {\n    None: (\n        {'code': 'en'},\n        {'code': 'fr'},\n        {'code': 'es'},\n    ),\n    'default': {\n        'fallbacks': ['en'],\n        'hide_untranslated': False, \n    }\n}\n\n# myapp/models.py\nfrom django.db import models\nfrom django.utils.translation import gettext_lazy as _\nfrom parler.models import TranslatableModel, TranslatedFields\n\nclass Category(TranslatableModel):\n    translations = TranslatedFields(\n        name=models.CharField(_(\"Name\"), max_length=200, unique=True)\n    )\n    # Add a non-translatable field for demonstration\n    is_active = models.BooleanField(default=True)\n\n    def __str__(self):\n        return self.safe_translation_getter('name', any_language=True)\n\n# myapp/admin.py\nfrom django.contrib import admin\nfrom parler.admin import TranslatableAdmin\nfrom .models import Category\n\n@admin.register(Category)\nclass CategoryAdmin(TranslatableAdmin):\n    list_display = ('name', 'is_active',)\n    # Prepopulated fields (if you had a slug for example)\n    # prepopulated_fields = {'slug': ('name',)}\n\n# Example usage in a shell or view\nfrom django.utils import translation\n\n# Create a category\ncat = Category(is_active=True)\ncat.set_current_language('en')\ncat.name = \"Electronics\"\ncat.save()\n\ncat.set_current_language('fr')\ncat.name = \"Électronique\"\ncat.save()\n\n# Accessing translations\nwith translation.override('en'):\n    print(f\"English Name: {cat.name}\") # Output: English Name: Electronics\n\nwith translation.override('fr'):\n    print(f\"French Name: {cat.name}\") # Output: French Name: Électronique\n\n# Querying translated fields\n# All categories with name 'Electronics' in any language\nelectronics_en = Category.objects.translated(name='Electronics').first()\nprint(f\"Found in EN: {electronics_en.name}\")\n\n# Categories with active translations for current language or fallbacks\nactive_cats = Category.objects.active_translations().filter(is_active=True)\nfor c in active_cats:\n    print(f\"Active Category ({c.get_current_language()}): {c.name}\")\n","lang":"python","description":"To quickly get started with django-parler, first install it and add `'parler'` to your `INSTALLED_APPS`. Configure `LANGUAGES` and `PARLER_LANGUAGES` in `settings.py`. Define your models by inheriting from `TranslatableModel` and wrapping translatable fields in `TranslatedFields`. Register your model with `TranslatableAdmin` in `admin.py` for full administrative support. Access translations using `set_current_language()` or `django.utils.translation.override()` and query with `.translated()` or `.active_translations()` methods."},"warnings":[{"fix":"Manually create migration files. The process involves three distinct steps in order: 1. `CreateModel` for the new translation tables (manually adjust `bases` argument). 2. `RunSQL` operations to copy data from old fields to the new translation fields. 3. `RemoveField` to delete the original columns from the main model. Refer to the official 'Making existing fields translatable' guide.","message":"When migrating existing models to be translatable, directly running `makemigrations` and `migrate` can lead to various `TypeError`, `AttributeError`, or `DataError` exceptions. This is because `django-parler` stores translations in a separate table, and the ORM struggles with data migration for this change.","severity":"breaking","affected_versions":"All versions, especially when upgrading existing models."},{"fix":"Ensure all filters for translatable fields are applied within a single `.translated()` or `.active_translations()` call. For `.active_translations()`, always append `.distinct()` to avoid duplicates, e.g., `MyModel.objects.active_translations(title='Cheese').distinct()`.","message":"Querying translatable fields (e.g., using `.translated()` or `.active_translations()`) cannot be chained with multiple `.filter()` calls for translatable fields due to ORM restrictions. Doing so might lead to incorrect or incomplete results. Also, `.active_translations()` often returns duplicate objects if `distinct()` is not used.","severity":"gotcha","affected_versions":"All versions"},{"fix":"To make a translated field unique per language, add `meta={'unique_together': [('language_code', 'field_name')]}` to the `TranslatedFields` declaration. Example: `translations = TranslatedFields(title=models.CharField(..., unique=False), meta={'unique_together': [('language_code', 'title')]})`.","message":"Enforcing unique constraints on translated fields (e.g., a unique product `slug` across all languages) requires explicit configuration. Simply adding `unique=True` to `CharField` inside `TranslatedFields` will enforce uniqueness across *all* translations, not per language.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Use `obj.safe_translation_getter('field_name', any_language=True)` to safely retrieve a translated value, which will return `None` or an empty string if no translation exists. Alternatively, declare the `TranslatedField` with `any_language=True` at the model level for automatic fallback to any available language. Configure robust `PARLER_LANGUAGES` fallbacks in `settings.py`.","message":"Accessing a translated field when a translation is missing for the current language, and no fallbacks are defined or available, will raise a `TranslationDoesNotExist` exception (which inherits from `AttributeError`).","severity":"gotcha","affected_versions":"All versions"},{"fix":"As this appears to be a bug in specific releases, check the `django-parler` GitHub issues for patches or workarounds. Downgrading to a stable 2.x version or upgrading to a newer patch release (if available) might resolve it.","message":"There are reports of the admin JavaScript (parler.js) missing in version 2.3, which can break the functionality of `TranslatableAdmin` and prevent the display of language tabs in the Django admin interface.","severity":"breaking","affected_versions":"2.3 (and potentially other minor versions)"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"When performing `makemigrations` for an existing model becoming translatable, manually edit the generated migration file. In the `migrations.CreateModel` operation for the *translation* model, change the `bases` argument from `parler.models.TranslatedFieldsModelMixin` to `parler.models.TranslatableModel`.","cause":"Often occurs during migrations when converting an existing model to be translatable, if the migration file is not manually corrected to ensure the generated translation model inherits properly.","error":"TypeError: Translatable model <class '__fake__.YourModel'> does not appear to inherit from TranslatableModel"},{"fix":"Ensure you follow the recommended three-step manual migration process (Create, Copy, Remove) for existing models. This error is typically bypassed by using `migrations.RunSQL` for data copying instead of ORM-based `RunPython` operations that might trigger this issue.","cause":"This error can occur during data migration when converting existing models to use `django-parler` if the migration steps are not followed precisely, particularly when attempting to copy data before the translation model is fully established or when the ORM's data migration tries to access incorrect attributes.","error":"AttributeError: 'NoneType' object has no attribute 'get_all_fields'"},{"fix":"1. Verify `'parler'` is in `INSTALLED_APPS`. 2. Ensure your model's admin class inherits from `parler.admin.TranslatableAdmin`. 3. Check your `PARLER_LANGUAGES` settings in `settings.py` are correct and `LANGUAGE_CODE` and `LANGUAGES` are defined. 4. If using version 2.3, be aware of a known issue regarding missing admin JavaScript (`parler.js`), and check for updates or workarounds.","cause":"This can happen due to incorrect `INSTALLED_APPS` configuration, missing `TranslatableAdmin` registration, issues with `PARLER_LANGUAGES` settings, or a bug in `django-parler`'s admin JavaScript for certain versions.","error":"Translated fields or language tabs are not showing in the Django admin site after migration/setup."},{"fix":"Ensure that the `max_length` defined for the fields within `TranslatedFields` is sufficient to accommodate the existing data from the original (non-translated) fields. You may need to increase the `max_length` in your `TranslatedFields` definition before running the data migration, or truncate existing data if appropriate.","cause":"This error occurs during data migration when copying existing field data to the new translation table if the original field's content exceeds the `max_length` defined for the corresponding translated field.","error":"django.db.utils.DataError: value too long for type character varying(...)"}]}