Given a model named MainModel
and a RelatedModel
, where the later has a ForeignKey
field to MainModel
:
class
You need just simply override change_view
in ModelAdmin:
def change_view(self, request, object_id, form_url='', extra_context=None):
obj = self.model.objects.filter(pk=object_id).first()
if not obj:
self.inlines = []
else:
if obj.type is True:
self.inlines = [RelatedModel1InlineAdmin]
else:
self.inlines = [RelatedModel2InlineAdmin]
return super().change_view(request,object_id,form_url=form_url,extra_context=extra_context)
that's work for me.
From peeking at contrib.admin.options.py
Looks like you could override ModelAdmin.get_formsets
. Note that the admin site populates self.inline_instances
at __init__
, so you probably want to follow and not instantiate your inlines over and over. I'm not sure how expensive it is : )
def get_formsets(self, request, obj=None):
if not obj:
return [] # no inlines
elif obj.type == True:
return [MyInline1(self.model, self.admin_site).get_formset(request, obj)]
elif obj.type == False:
return [MyInline2(self.model, self.admin_site).get_formset(request, obj)]
# again, not sure how expensive MyInline(self.model, self.admin_site) is.
# the admin does this once. You could instantiate them and store them on the
# admin class somewhere to reference instead.
The original admin get_formsets
uses generators - you could too to more closely mimic the original:
def get_formsets(self, request, obj=None):
for inline in self.inline_instances:
yield inline.get_formset(request, obj)
I realize this question's a bit old and the codebase has changed a bit; there's a cleanish point to override things at now: get_inline_instances
. You can do this:
class MainModelAdmin(models.ModelAdmin):
inlines = [RelatedModel1InlineAdmin,RelatedModel2InlineAdmin]
def get_inline_instances(self, request, obj=None):
#Return no inlines when obj is being created
if not obj:
return []
unfiltered = super(MainModelAdmin, self).get_inline_instances(request, obj)
#filter out the Inlines you don't want
if obj.type:
return [x for x in unfiltered if isinstance(x,RelatedModel1InlineAdmin)]
else:
return [x for x in unfiltered if isinstance(x,RelatedModel2InlineAdmin)]
This worked for me while searching for an answer to the same problem in this old post.
Expanding upon darklow's answer , I think you can simply override get_inline_instances
completely and add an extra check based on your type.
Add a boolean type check method in your model
class MainModel(models.Model):
name = models.CharField(max_length=50)
type = models.BooleanField()
def is_type1(self):
return type=="some value"
def is_type2(self):
return type=="some value"
Add inline instance base on type check - Simply copy and paste the get_inline_insances method from the parent class into your admin.ModelAdmin class and add the if block to check the model type as shown below
class MyModelAdmin(admin.ModelAdmin):
inlines = [RelatedModel1, RelatedModel2]
def get_inline_instances(self, request, obj=None):
inline_instances = []
if not obj:
return []
for inline_class in self.inlines:
inline = inline_class(self.model, self.admin_site)
if request:
if not (inline.has_add_permission(request) or
inline.has_change_permission(request, obj) or
inline.has_delete_permission(request, obj)):
continue
if not inline.has_add_permission(request):
inline.max_num = 0
if obj.is_type1() and isinstance(inline,RelatedModel1InlineAdmin):
inline_instances.append(inline)
if obj.is_type2() and isinstance(inline,RelatedModel2InlineAdmin):
inline_instances.append(inline)
return inline_instances
@Yuji 'Tomita' Tomitayou the idea was good, i had the same but once trying, i realized you must also remove specific key from self.inlines
because in change_view
and add_view
method self.get_inline_instances(request)
is called before get_formsets()
. Therefore i moved inlines handling to get_form()
method.
Here is how i sucessfully did it:
class SampleAdmin(ModelAdmin):
inlines = []
def get_inlines(self):
return [SampleInline, SampleInline2]
def get_form(self, request, obj=None, **kwargs):
# due to django admin form fields caching you must
# redefine inlines on every `get_form()` call
if (obj): self.inlines = self.get_inlines()
for inline in self.inlines:
# Here change condition based on your needs and manipulate
# self.inlines as you like (remove, change, etc).
# I used inline.__name__ to detect if this is correct inline
# for my obj
if obj.CONDITION:
self.inlines.remove(inline)
return super(SampleAdmin, self).get_form(request, obj, **kwargs)
Here is a piece of code I wrote when I was faced with the same problem. It is a bit brute force style, I guess, but is very agile and should suit all cases.
class MyModelAdmin(admin.ModelAdmin):
def __init__(self, *args, **kwargs):
super(MyModelAdmin, self).__init__(*args, **kwargs)
self.inline_instances_hash = {}
for inline_class in self.inlines:
for inline_instance in self.inline_instances:
if isinstance(inline_instance, inline_class):
break
self.inline_instances_hash[inline_class] = inline_instance
def get_inline_instance(self, inline_class):
return self.inline_instances_hash[inline_class]
def get_form(self, request, obj=None, **kwargs):
if obj:
self.inline_instances = []
if self.CONDITION:
self.inline_instances.append(self.get_inline_instance(
THE_INLINE_CLASS_I_WANT))
#...
else:
self.inline_instances = self.inline_instances_hash.values()