Django 1.7 removing Add button from inline form

后端 未结 4 1692

I\'m having problems achieving a (probably) rather simple task. I have fully modifiable models (Prodotto, Comune) which are shown as \"addable\" fields, as shown in picture

相关标签:
4条回答
  • 2020-12-16 01:09

    After a couple of intense days, I finally managed to find a way to achieve that.

    A simple trick such as this is more than enough when dealing with this problem within ModelAdmin subclasses (see ClienteAdmin in my code above), so here's the class version without adding capabilities for "Prodotto" field:

    @admin.register(Cliente)
    class ClienteAdmin(admin.ModelAdmin):
        list_display = [
            'ragione_sociale', 'forma_societaria', 'titolare', 'partita_iva', ]
        list_filter = ['forma_societaria', ]
        search_fields = ['ragione_sociale', ]
        inlines = [RecapitoInline, SedeInline]
        def get_form(self, request, obj=None, **kwargs):    # Just added this override
            form = super(ClienteAdmin, self).get_form(request, obj, **kwargs)
            form.base_fields['prodotto'].widget.can_add_related = False
            return form
    

    The real pain comes when dealing with inline classes (TabularInline, StackedInline), as the get_form() function seems not to be called at all, so the previous way won't work.

    Explaining all my previous attempts would take too long, and I'm probably not even good enough with Django yet to tell why they didn't work. So let's get straight to the solution, which in fact is not even that complicated.

    I subclassed django.contrib.admin.widgets.RelatedFieldWidgetWrapper widget and overridden its render method, so that it doesn't append the "add-another" anchor to the output. Easily done by commenting out a few lines. After doing so, monkeypatching the original RelatedFieldWidgetWrapper with my own version (django.contrib.admin.widgets.RelatedFieldWidgetWrapper = NoAddingRelatedFieldWidgetWrapper) made the trick.

    Clearly, for it to work I had to add the import line in the admin.py:

    from .widgets import NoAddingRelatedFieldWidgetWrapper

    widgets.py

    import django.contrib.admin.widgets
    from django.utils.safestring import mark_safe
    
    
    class NoAddingRelatedFieldWidgetWrapper(django.contrib.admin.widgets.RelatedFieldWidgetWrapper):
    
        def render(self, name, value, *args, **kwargs):
            from django.contrib.admin.views.main import TO_FIELD_VAR
            rel_to = self.rel.to
            info = (rel_to._meta.app_label, rel_to._meta.model_name)
            self.widget.choices = self.choices
            output = [self.widget.render(name, value, *args, **kwargs)]
            '''
            if self.can_add_related:
                related_url = reverse('admin:%s_%s_add' % info, current_app=self.admin_site.name)
                url_params = '?%s=%s' % (TO_FIELD_VAR, self.rel.get_related_field().name)
                # TODO: "add_id_" is hard-coded here. This should instead use the
                # correct API to determine the ID dynamically.
                output.append('<a href="%s%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> '
                              % (related_url, url_params, name))
                output.append('<img src="%s" width="10" height="10" alt="%s"/></a>'
                              % (static('admin/img/icon_addlink.gif'), _('Add Another')))
            '''
            return mark_safe(''.join(output))
    
    # Monkeypatch
    django.contrib.admin.widgets.RelatedFieldWidgetWrapper = NoAddingRelatedFieldWidgetWrapper
    

    For the sake of completion, here's the final version of the related admin.py:

    admin.py

    from django.contrib import admin
    import django.contrib.admin.widgets
    
    from django.db import models
    
    from .models import Cliente, Prodotto, Sede
    from apps.recapito.models import RecapitoCliente
    from .widgets import NoAddingRelatedFieldWidgetWrapper
    
    
    class SedeInline(admin.TabularInline):
        model = Sede
        extra = 1
    
        def provincia(self, obj):
            return obj.comune.provincia
    
        readonly_fields = ['provincia', ]
    
    
    class RecapitoInline(admin.TabularInline):
        model = RecapitoCliente
        extra = 1
        readonly_fields = ['cliente', 'tipo', 'recapito', ]
    
    
    @admin.register(Cliente)
    class ClienteAdmin(admin.ModelAdmin):
        list_display = [
            'ragione_sociale', 'forma_societaria', 'titolare', 'partita_iva', ]
        list_filter = ['forma_societaria', ]
        search_fields = ['ragione_sociale', ]
        inlines = [RecapitoInline, SedeInline]
        def get_form(self, request, obj=None, **kwargs):
            form = super(ClienteAdmin, self).get_form(request, obj, **kwargs)
            form.base_fields['prodotto'].widget.can_add_related = False
            return form
    

    Shall anyone come out with any better solution, I'll gladly accept it in place of mine.

    0 讨论(0)
  • 2020-12-16 01:10

    To remove the "Add another" option, please add the below method in admin inline class.

    def has_add_permission(self, request):
        return False
    

    Similarly if you want to disable "Delete?" option, add the following method in admin inline class.

    def has_delete_permission(self, request, obj=None):
        return False
    
    0 讨论(0)
  • 2020-12-16 01:16

    I think this is a less hacky solution than the one you ended up with. It worked for me, anyway.

    Basically, it's the inline equivalent of what you suggested doing with the overriding the get_form method of ModelAdmin. Here we override get_formset in the inline class, get the form off the formset, and do the exact same thing. Seems to work fine, at least in 1.9, which I am using.

    class VersionEntryInline(admin.TabularInline):
        template = 'admin/edit_inline/tabular_versionentry.html'
        model = VersionEntry
        extra = 0
    
        def get_formset(self, request, obj=None, **kwargs):
            """
            Override the formset function in order to remove the add and change buttons beside the foreign key pull-down
            menus in the inline.
            """
            formset = super(VersionEntryInline, self).get_formset(request, obj, **kwargs)
            form = formset.form
            widget = form.base_fields['project'].widget
            widget.can_add_related = False
            widget.can_change_related = False
            widget = form.base_fields['version'].widget
            widget.can_add_related = False
            widget.can_change_related = False
            return formset
    
    0 讨论(0)
  • 2020-12-16 01:17

    There is actually a clean solution for this:

    class YourInline(admin.TabularInline):
        extra = 0
        max_num=0
    
    0 讨论(0)
提交回复
热议问题