Django Admin Integration
========================
Django i18n Fields provides comprehensive Django admin support with beautiful tab and dropdown interfaces for managing multilingual content.
Quick Setup
-----------
Basic Admin Integration
~~~~~~~~~~~~~~~~~~~~~~~~
Use the ``LocalizedFieldsAdminMixin`` to automatically enable localized field widgets:
.. code-block:: python
# admin.py
from django.contrib import admin
from i18n_fields import LocalizedFieldsAdminMixin
from .models import Article
@admin.register(Article)
class ArticleAdmin(LocalizedFieldsAdminMixin, admin.ModelAdmin):
list_display = ['title', 'created_at']
fields = ['title', 'content', 'published']
That's it! All localized fields will automatically get tab/dropdown widgets.
Display Modes
-------------
Tab Mode (Default)
~~~~~~~~~~~~~~~~~~
Shows language tabs above each localized field:
.. code-block:: python
@admin.register(Article)
class ArticleAdmin(LocalizedFieldsAdminMixin, admin.ModelAdmin):
localized_fields_display = 'tab' # or omit (tab is default)
**Features:**
- Tabs for each configured language
- Active tab highlighted
- Easy switching between languages
- Best for 2-6 languages
Dropdown Mode
~~~~~~~~~~~~~
Shows a dropdown menu to select the language:
.. code-block:: python
@admin.register(Article)
class ArticleAdmin(LocalizedFieldsAdminMixin, admin.ModelAdmin):
localized_fields_display = 'dropdown'
**Features:**
- Dropdown menu with language names
- More compact for many languages
- Good for 7+ languages
Global Configuration
~~~~~~~~~~~~~~~~~~~~
Set the default display mode in settings:
.. code-block:: python
# settings.py
I18N_FIELDS = {
"DISPLAY": "dropdown", # or "tab"
}
List Display
------------
Localized fields work seamlessly in list_display:
.. code-block:: python
@admin.register(Article)
class ArticleAdmin(LocalizedFieldsAdminMixin, admin.ModelAdmin):
list_display = ['title', 'author', 'created_at', 'published']
list_filter = ['published', 'created_at']
search_fields = ['title__en', 'title__es']
The mixin automatically shows translated values in the current language.
Readonly Fields
---------------
Read-only localized fields display all translations:
.. code-block:: python
@admin.register(Article)
class ArticleAdmin(LocalizedFieldsAdminMixin, admin.ModelAdmin):
readonly_fields = ['slug', 'created_at']
fields = ['title', 'slug', 'content', 'created_at']
Read-only fields show a tab/dropdown interface with all translations visible but not editable.
Fieldsets
---------
Organize localized fields in fieldsets:
.. code-block:: python
@admin.register(Article)
class ArticleAdmin(LocalizedFieldsAdminMixin, admin.ModelAdmin):
fieldsets = [
('Basic Information', {
'fields': ['title', 'slug', 'author']
}),
('Content', {
'fields': ['content', 'excerpt'],
'classes': ['wide']
}),
('Metadata', {
'fields': ['published', 'created_at'],
'classes': ['collapse']
}),
]
Inline Admin
------------
Localized fields work in inline admin:
.. code-block:: python
class ChapterInline(LocalizedFieldsAdminMixin, admin.TabularInline):
model = Chapter
fields = ['title', 'order']
extra = 1
@admin.register(Book)
class BookAdmin(LocalizedFieldsAdminMixin, admin.ModelAdmin):
inlines = [ChapterInline]
Search
------
Enable search by specific languages:
.. code-block:: python
@admin.register(Article)
class ArticleAdmin(LocalizedFieldsAdminMixin, admin.ModelAdmin):
search_fields = [
'title__en', # Search English titles
'title__es', # Search Spanish titles
'content__en', # Search English content
'author__name', # Regular field
]
Filtering
---------
Filter by localized field values:
.. code-block:: python
from django.contrib import admin
from i18n_fields import LocalizedFieldsAdminMixin
class PublishedFilter(admin.SimpleListFilter):
title = 'published status'
parameter_name = 'published'
def lookups(self, request, model_admin):
return [
('yes', 'Published'),
('no', 'Not Published'),
]
def queryset(self, request, queryset):
if self.value() == 'yes':
return queryset.filter(published=True)
if self.value() == 'no':
return queryset.filter(published=False)
@admin.register(Article)
class ArticleAdmin(LocalizedFieldsAdminMixin, admin.ModelAdmin):
list_filter = [PublishedFilter, 'created_at']
Ordering
--------
Order by localized fields in admin:
.. code-block:: python
from i18n_fields import L
@admin.register(Article)
class ArticleAdmin(LocalizedFieldsAdminMixin, admin.ModelAdmin):
def get_queryset(self, request):
qs = super().get_queryset(request)
# Order by title in current language
return qs.order_by(L('title'))
Custom Widgets
--------------
Override widgets for specific fields:
.. code-block:: python
from django import forms
from i18n_fields.widgets import AdminLocalizedFieldWidget
@admin.register(Article)
class ArticleAdmin(LocalizedFieldsAdminMixin, admin.ModelAdmin):
def formfield_for_dbfield(self, db_field, request, **kwargs):
if db_field.name == 'title':
kwargs['widget'] = AdminLocalizedFieldWidget(
display_mode='dropdown'
)
return super().formfield_for_dbfield(db_field, request, **kwargs)
Form Validation
---------------
Custom validation works normally:
.. code-block:: python
from django import forms
class ArticleAdminForm(forms.ModelForm):
class Meta:
model = Article
fields = '__all__'
def clean_title(self):
title = self.cleaned_data['title']
# Validate English title exists
if not title.get('en'):
raise forms.ValidationError('English title is required')
return title
@admin.register(Article)
class ArticleAdmin(LocalizedFieldsAdminMixin, admin.ModelAdmin):
form = ArticleAdminForm
Actions
-------
Admin actions work with localized fields:
.. code-block:: python
@admin.register(Article)
class ArticleAdmin(LocalizedFieldsAdminMixin, admin.ModelAdmin):
actions = ['publish_articles', 'duplicate_to_spanish']
def publish_articles(self, request, queryset):
queryset.update(published=True)
self.message_user(request, f'{queryset.count()} articles published')
def duplicate_to_spanish(self, request, queryset):
for article in queryset:
# Copy English to Spanish
title = article.title
if title.get('en') and not title.get('es'):
title.set('es', title.get('en'))
article.title = title
article.save()
self.message_user(request, 'Duplicated to Spanish')
Complete Example
----------------
Here's a comprehensive example:
.. code-block:: python
# admin.py
from django.contrib import admin
from django.utils.html import format_html
from i18n_fields import LocalizedFieldsAdminMixin, L
from .models import Article, Category
class CategoryInline(admin.TabularInline):
model = Article.categories.through
extra = 1
@admin.register(Article)
class ArticleAdmin(LocalizedFieldsAdminMixin, admin.ModelAdmin):
# Display mode
localized_fields_display = 'tab'
# List display
list_display = [
'title',
'author',
'published_status',
'created_at'
]
list_filter = ['published', 'created_at', 'author']
search_fields = ['title__en', 'title__es', 'content__en']
# Fieldsets
fieldsets = [
('Content', {
'fields': ['title', 'slug', 'content', 'excerpt']
}),
('Metadata', {
'fields': ['author', 'published', 'created_at', 'updated_at'],
'classes': ['collapse']
}),
]
readonly_fields = ['slug', 'created_at', 'updated_at']
inlines = [CategoryInline]
# Custom methods
def published_status(self, obj):
if obj.published:
return format_html(
'✓ Published'
)
return format_html(
'✗ Draft'
)
published_status.short_description = 'Status'
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.select_related('author').order_by(L('title'))
Styling
-------
The admin widgets come with default styles, but you can customize them:
.. code-block:: python
# admin.py
@admin.register(Article)
class ArticleAdmin(LocalizedFieldsAdminMixin, admin.ModelAdmin):
class Media:
css = {
'all': ('css/custom_admin.css',)
}
.. code-block:: css
/* static/css/custom_admin.css */
.i18n-tabs {
background-color: #f0f0f0;
border-radius: 5px;
}
.i18n-tab.active {
background-color: #0066cc;
color: white;
}
Best Practices
--------------
1. **Use the Mixin**
Always include ``LocalizedFieldsAdminMixin`` for localized fields:
.. code-block:: python
# Good
class ArticleAdmin(LocalizedFieldsAdminMixin, admin.ModelAdmin):
pass
# Missing features without mixin
class ArticleAdmin(admin.ModelAdmin):
pass
2. **Configure list_display**
Include localized fields in list_display to show translated content:
.. code-block:: python
list_display = ['title', 'created_at'] # Shows translated title
3. **Search by Language**
Specify languages in search_fields for better search:
.. code-block:: python
search_fields = ['title__en', 'title__es', 'content__en']
4. **Choose Appropriate Display Mode**
- Use **tab mode** for 2-6 languages
- Use **dropdown mode** for 7+ languages
5. **Order Appropriately**
Use ``L()`` expression for ordering in the current language:
.. code-block:: python
def get_queryset(self, request):
return super().get_queryset(request).order_by(L('title'))
Troubleshooting
---------------
**Widgets Not Showing**
Make sure ``LocalizedFieldsAdminMixin`` is included and comes before ``admin.ModelAdmin``:
.. code-block:: python
# Correct
class ArticleAdmin(LocalizedFieldsAdminMixin, admin.ModelAdmin):
pass
**Static Files Not Loading**
Run collectstatic:
.. code-block:: bash
python manage.py collectstatic
**Tabs Not Switching**
Check browser console for JavaScript errors. Ensure jQuery is loaded.
Next Steps
----------
- :doc:`drf-integration` - Integrate with Django REST Framework
- :doc:`advanced-queries` - Advanced querying techniques
- :doc:`../reference/admin` - Complete admin reference