Django Admin: Default values in StackedInline / TabularInline

8k views Asked by At

I'm creating a website and it needs support for internationalization. The default languages are Portuguese, English and Spanish. I'm using the django-i18nmodel and so far it works great.

When the admin wants to create a product, using django-admin, by default I create 3 inLine items of the model ProductI18N.

class LanguageStackedInline(admin.StackedInline):
    model = ProductI18N
    extra = 1

I want to create these 3 rows with the 3 default languages I mentioned above (pt-PT, en-US, es-ES). I know that in the Model I can only set a default value.

Does Django provide an easy way of doing this?

2

There are 2 answers

0
uranusjr On

Provide a custom formset class for the inline admin:

from django.forms.models import BaseInlineFormSet

class LanguageInlineFormSet(BaseInlineFormSet):
    def __init__(self, *args, **kwargs):
        super(LanguageInlineFormSet, self).__init__(*args, **kwargs)

        # Assuming the field you want to populate to is called "name"
        self.initial = [
            {'name': 'pt-PT'}, {'name': 'en-US'}, {'name': 'es-ES'}
        ]

class LanguageStackedInline(admin.StackedInline):
    model = ProductI18N
    extra = 3    # You said you need 3 rows
    formset = LanguageInlineFormSet

You can take a look at the documentation on admin and inline formsets for more notes on customization.

1
AudioBubble On

I'd like to thank uranusjr for giving me a hint for this solution. His answer did not work for me but here is what worked:

class LanguageInlineFormSet(BaseInlineFormSet):
    def __init__(self, *args, **kwargs):
        kwargs['initial'] = [
            {'name': 'pt-PT'}, {'name': 'en-US'}, {'name': 'es-ES'}
        ]
        super(LanguageInlineFormSet, self).__init__(*args, **kwargs)

# Rest of the code as per @uranusjr's answer
class LanguageStackedInline(admin.StackedInline):
    model = ProductI18N
    extra = 3    # You said you need 3 rows
    formset = LanguageInlineFormSet

I kept the 'name' key as is for easy comparison.

To explain in more detail, the BaseInlineFormSet takes the initial argument as documented here:

https://docs.djangoproject.com/en/dev/topics/forms/formsets/#formsets-initial-data

So simply adding it to kwargs in the overloaded constructor works well.

EDIT: Let me also share the code I actually use in my app:

from django.conf import settings
from django.forms.models import BaseInlineFormSet

from myapp.models import MyI18N

class MyI18NInlineFormset(BaseInlineFormSet):
    def __init__(self, *args, **kwargs):
        kwargs['initial'] = [{'i18n_language': lang[0]}
                             for lang in settings.LANGUAGES
                             if lang[0] != settings.LANGUAGE_CODE]
        super(MyI18NInlineFormset, self).__init__(*args, **kwargs)

class MyI18NInline(admin.StackedInline):
    model = MyI18N
    extra = max_num = len(settings.LANGUAGES) - 1
    formset = MyI18NInlineFormset

This generates one form per each non-default language. It's not perfect as it doesn't take into account cases where one of the non-default languages is already saved, but it gives me a good starting point.