Django admin different inlines for change and add view
Asked Answered
S

6

15

I need separate views for add and change page. In add page I'd like to exclude some fields from inline formset. I've prepared two TabularInline classes, one of them contains property 'exclude'. I tried to use them as follows:

class BoxAdmin(admin.ModelAdmin):
    def change_view(self, request, obj_id):
        self.inlines=[ItemChangeInline,]
        return super(BoxAdmin, self).change_view(self.request, obj_id)
    def add_view(self, request):
        self.inlines=[ItemAddInline,]
        return super(BoxAdmin, self).add_view(self, request)

with no effect (no inline is shown at all).

Speight answered 10/2, 2010 at 8:58 Comment(0)
B
21

It works with Django 1.5+ and seems fine & elegant:

// admin.py
class BoxAdmin(ModelAdmin):

    inlines = ()

    def change_view(self, request, object_id, form_url='', extra_context=None):
        self.inlines = (ItemChangeInline, )
        return super(BoxAdmin, self).change_view(request, object_id)

    def add_view(self, request, form_url='', extra_context=None):
        self.inlines = (ItemAddInline, )
        return super(BoxAdmin, self).add_view(request)

hope it can be useful for anyone

Batfowl answered 24/5, 2013 at 11:21 Comment(1)
I used it verbatim and it works, I replaced though the two returns with return super(BoxAdmin, self).change_view(request, object_id, form_url, extra_context) and return super(BoxAdmin, self).add_view(request, form_url, extra_context)Romaineromains
S
7

Here is the code that seems to be working:

class BoxAdmin(admin.ModelAdmin):
   def change_view(self, request, obj_id):
        self.inlines=[ItemChangeInline,]
        for inline_class in self.inlines:
            inline_instance = inline_class(self.model, self.admin_site)
            self.inline_instances.append(inline_instance)
        return super(BoxAdmin, self).change_view(request, obj_id)
    def add_view(self, request):
        self.inlines=[ItemAddInline,]
        for inline_class in self.inlines:
            inline_instance = inline_class(self.model, self.admin_site)
            self.inline_instances.append(inline_instance)
        return super(BoxAdmin, self).add_view(request)

However, this looks inelegant, cause this part:

            for inline_class in self.inlines:
            inline_instance = inline_class(self.model, self.admin_site)
            self.inline_instances.append(inline_instance)

is a copy-paste from init method of admin.ModelAdmin (so it is run twice).

Speight answered 10/2, 2010 at 10:24 Comment(0)
G
1

Why in add_view you have .add_view(self, request) and in change view you have .change_view(self.request, ..)? I believe, you don't need self in add_view, since you use super.

Gypsie answered 10/2, 2010 at 9:25 Comment(0)
H
0

I had a situation where I needed to show an Inline based on the admin site that you were on for a given story.

Expanding on alekwisnia's answer, I was able to get dynamic inlines working for Django 1.3 using the following code:

In highlights/admin.py

class HighlightInline(generic.GenericTabularInline):
    model = Highlight
    extra = 1
    max_num = 4
    fields = ('order', 'highlight')
    template = 'admin/highlights/inline.html'

class HighlightAdmin(admin.ModelAdmin):
    def regulate_highlight_inlines(self):
        highlights_enabled = Setting.objects.get_or_default('highlights_enabled', default='')
        highlight_inline_instance = HighlightInline(self.model, self.admin_site)
        highlight_found = any(isinstance(x, HighlightInline) for x in self.inline_instances)
        if highlights_enabled.strip().lower() == 'true':
            if not highlight_found:
                self.inline_instances.insert(0, highlight_inline_instance)
        else:
            if highlight_found:
                self.inline_instances.pop(0)
        print self.inline_instances

    def change_view(self, request, object_id, form_url='', extra_context=None):
        self.regulate_highlight_inlines()
        return super(HighlightAdmin, self).change_view(request, object_id)

    def add_view(self, request, form_url='', extra_context=None):
        self.regulate_highlight_inlines()   
        return super(HighlightAdmin, self).add_view(request, form_url, extra_context)

In story/admin.py

class StoryAdmin(HighlightAdmin):

One thing to note is that I'm not merely manipulating inline classes(HighlightInline) but rather, I'm changing inline instances(HighlightInline(self.model, self.admin_site)). This is because django has already constructed a list of inline instances based on a list of inline classes during the initial construction of the admin class.

Hesitant answered 15/6, 2014 at 14:27 Comment(0)
D
0

Another solution to Django 1.3

class BoxAdmin(admin.ModelAdmin):

    def change_view(self, request, object_id, form_url='', extra_context=None):
        self.inline_instances = [ItemChangeInline(self.model, self.admin_site)]
        return super(BoxAdmin, self).change_view(request, object_id, extra_context)

    def add_view(self, request, form_url='', extra_context=None):
        self.inline_instances = [ItemAddInline(self.model, self.admin_site)]
        return super(BoxAdmin, self).add_view(request, form_url, extra_context)
Dryden answered 29/3, 2016 at 14:8 Comment(0)
E
0

Inspired by you guys answer, I was able to add more custom views to the admin.site.

Many times, just want add and change pages of different settings, not real extra views

# admin.py
class FooAdmin(admin.ModelAdmin):
    ....
    def edit_tag(self, obj):              # add a Link tag to change-list page
        return mark_safe('<a href="{}?edit=True">Edit</a>'.format(obj.get_absolute_url()))

    edit_tag.short_description = u'Extra Action'

    def change_view(self, request, object_id, form_url='', extra_context=None):
        if request.GET.get('edit', False):
            self.readonly_fields = (
                'total_amount',
            )
            self.inlines = []
        else:
            self.readonly_fields = (
                'name', 'client', 'constructor', 'total_amount'
            )
            self.inlines = [TransactionInline]
        return super(ProjectAdmin, self).change_view(request, object_id)

    def add_view(self, request, form_url='', extra_context=None):
        self.readonly_fields = (
            'total_amount',
        )
        self.inlines = []
        return super(ProjectAdmin, self).add_view(request)

After this I'll have three views: enter image description here

  1. add view - without inline formset, no need to add related objects. enter image description here

  2. change view 1 - with inline formset, only for adding inline data(related objects), the the object's field is readonly. enter image description here

  3. change view 2 - without inline formset, only for changing the object. enter image description here

Really simple, and we can do more, thanks everyone.

Equalitarian answered 31/3, 2019 at 9:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.