Siva Arunachalam
Siva Arunachalam

Reputation: 7740

Django - Admin - Inline - 'extra' value based on some condition

Is it possible to dynamically set the 'extra' option in the Django Admin Inline?

For example, If Student class have Address class as Inline. If there is no Address inline's associated with Student, then extra =1. If there is any Address inline's associated with Student, then extra =0.

Upvotes: 3

Views: 4308

Answers (4)

Langlyin
Langlyin

Reputation: 156

Just simply override the get_extra method. The following example set extra to 0 for the add view and 10 for the edit view.

class MyInline(admin.TabularInline):
    model = MyModel

    def get_extra(self, request, obj=None, **kwargs):
        return 0 if obj else 10

Upvotes: 7

Shishir Biyyala
Shishir Biyyala

Reputation: 71

You can just leverage inheritance..

// based on some condition
kwargs['extra'] = something
.........
return super(*******Inline, self).get_formset(request, obj, **kwargs) // 'defaults.update(kwargs)' takes care of the dynamic overriding 

The get_formset method from my project :

def get_formset(self, request, obj=None, **kwargs):
    ## Put in your condition here and assign extra accordingly
    if obj is None:
        return super(ImageInline, self).get_formset(request, obj, **kwargs)
    current_topic = TopicPage.objects.get(pk = obj.id)
    topic_images = ThruImage.objects.filter(topic = current_topic)

    kwargs['extra'] = 0
    if len(topic_images) <= 3:
        kwargs['extra'] = 3 - len(topic_images)
    return super(ImageInline, self).get_formset(request, obj, **kwargs)

This is of course, useful only for simple conditionals based off the parent model object ..

Upvotes: 3

Rabih Kodeih
Rabih Kodeih

Reputation: 9521

You just monkey patch django's (1.3.1) source code as follows:

First add the following code to your app:

from django.forms.models import inlineformset_factory
from django.contrib.admin.util import flatten_fieldsets
from django.utils.functional import curry
from django.contrib.admin.options import InlineModelAdmin

class MyInlineModelAdmin(InlineModelAdmin):
    #extra = 1
    def get_formset(self, request, obj=None, **kwargs):
        """Returns a BaseInlineFormSet class for use in admin add/change views."""
        if self.declared_fieldsets:
            fields = flatten_fieldsets(self.declared_fieldsets)
        else:
            fields = None
        if self.exclude is None:
            exclude = []
        else:
            exclude = list(self.exclude)
        exclude.extend(kwargs.get("exclude", []))
        exclude.extend(self.get_readonly_fields(request, obj))
        # if exclude is an empty list we use None, since that's the actual
        # default
        exclude = exclude or None
        if obj and hasattr(obj, 'id'): # <<=======================================
            _extra = 0
        else:
            _extra = self.extra
        defaults = {
            "form": self.form,
            "formset": self.formset,
            "fk_name": self.fk_name,
            "fields": fields,
            "exclude": exclude,
            "formfield_callback": curry(self.formfield_for_dbfield, request=request),
            "extra": _extra,
            "max_num": self.max_num,
            "can_delete": self.can_delete,
        }
        defaults.update(kwargs)
        return inlineformset_factory(self.parent_model, self.model, **defaults)

class MyTabularInline(MyInlineModelAdmin):
    template = 'admin/edit_inline/tabular.html'

and assuming your models are something like:

class ContainerModel(models.Model):
    pass #etc...

class ListModel(models.Model):
    pass #etc...

then change your admins to:

class ListModelInline(MyTabularInline): # <<=================================
    model = MyModel

class ContainerModelAdmin(admin.ModelAdmin):
    inlines = (ListModelInline,)

admin.site.register(ContainerModel, ContainerModelAdmin)
#etc...

Upvotes: 1

Marcus Whybrow
Marcus Whybrow

Reputation: 19998

Not sure if it would work and I am not too familiar with inlines and this extra attribute, but you could subclass django.contrib.admin.InlineModelAdmin and replace the InlineModelAdmin.extra attribute with a python property:

from django.contrib import admin
from myproject.myapp.models import MyInlineModel

class DynamicExtraInlineModelAdmin(admin.InlineModelAdmin):

    @property
    def extra():
        return 1 if some_logic else 0

admin.site.register(MyInlineModel, DynamicExtraInlineModelAdmin)

Upvotes: 2

Related Questions