Given a model named MainModel
and a RelatedModel
, where the later has a ForeignKey
field to MainModel
:
class MainModel(models.Model):
name = models.CharField(max_length=50)
type = models.BooleanField()
class RelatedModel1(models.Model):
main = models.ForeingKey(MainModel):
name = models.CharField(max_length=50)
class RelatedModel2(models.Model):
main = models.ForeingKey(MainModel):
name = models.CharField(max_length=50)
and the corresponding ModelAdmin classes:
class RelatedModel1InlineAdmin(admin.TabularInline):
model = RelatedModel1
class RelatedModel2InlineAdmin(admin.TabularInline):
model = RelatedModel2
class MainModel(admin.ModelAdmin):
inlines = [RelatedModel1, RelatedModel2]
And that's the default behavior, you get two inlines, one for every related model. The question is how to hide completely all the inlines when the MainModel
instance is being created (the ModelAdmin
's add_view
), and to show the inlines for RelatedModel1
when the type
field of the MainModel
instance is True
, and show the inlines for RelatedModel2
when False
.
I was going to create a descriptor for the ModelAdmin.inline_instances
attribute, but I realized that I need access to the object instance being edited, but it is passed around as parameters.
Any help?
Thanks!
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.
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
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)]
@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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With