Django 1.7 suppression du bouton Ajouter du formulaire en ligne

j'ai des problèmes pour accomplir une tâche (probablement) assez simple. J'ai des modèles entièrement modifiables (Prodotto, Comune) qui sont montrés comme des champs "addable", comme montré dans la photo ci-dessous. Ce que je préfère ne pas voir est le bouton + (add) pour de tels champs, donc pour supprimer leur propriété "addable" dans cette forme. J'ai essayé de définir has_add_permission=False dans les deux modèles, mais il serait impossible d'ajouter de nouveaux objets à ces modèles complètement, non seulement dans ce formulaire.

Comment faire?

EDIT: Pour préciser mon besoin, j'aimerais ne PAS avoir de "+"s à côté des champs de la FK modèles, mais je ne encore voulez être en mesure d'ajouter une nouvelle inlines. Pour être aussi clair que possible, comme je l'ai écrit dans un commentaire, considérant un scénario tel que celui-ci: https://code.djangoproject.com/attachment/ticket/20367/django_custom_user_admin_form.png I il suffit de faire supprimer les "+"à côté des groupes et du pays.

CODE EXISTANT:

models.py (de la demande en cause):

from django.db import models

from smart_selects.db_fields import ChainedForeignKey

from apps.comune.models import Comune, Cap


class Prodotto(models.Model):
    SETTORE_CHOICES = (
        ('CAL', 'Accessori calzature'),
        ('ALI', 'Alimentari'),
        ('ARA', 'Arredamenti e accessori'),
        ('AEM', 'Auto e moto'),
        ('CAL', 'Calzature'),
        ('CEG', 'Cartaria e grafica'),
        ('CEP', 'Concerie e pelletterie'),
        ('EDI', 'Edilizia'),
        ('INV', 'Industrie varie'),
        ('IST', 'Istruzione'),
        ('MDC', 'Materiali da costruzione'),
        ('MMC', 'Metalmeccanica'),
        ('SEI', 'Serramenti e infissi'),
        ('STM', 'Strumenti musicali'),
        ('TEI', 'Terziario innovativo'),
        ('TAB', 'Tessile abbigliamento'),
        ('TCP', 'Trasporto cose e persone'),
        ('VAR', 'Vari'),
    )
    nome = models.CharField(max_length=100)
    settore = models.CharField(max_length=40, choices=SETTORE_CHOICES)

    class Meta:
        verbose_name_plural = "prodotti"
        verbose_name = "prodotto"
        ordering = ['nome']

    def __unicode__(self):
        return self.nome.capitalize()


class Cliente(models.Model):
    TIPOLOGIA_CHOICES = (
        ('AR', 'Artigiano'),
        ('CO', 'Commerciante'),
        ('GI', 'Grande impresa'),
        ('PI', 'Piccola impresa'),
    )
    FORMA_SOCIETARIA_CHOICES = (
        ('SNC', 'S.n.c.'),
        ('SRL', 'S.r.l.'),
        ('SPA', 'S.p.A.'),
        ('SAS', 'S.a.s.'),
        ('COOP', 'Coop.A.r.l.'),
        ('DI', 'D.I.'),
        ('SCARL', 'S.c.a.r.l.'),
        ('SCPA', 'S.c.p.a.'),
    )
    SETTORE_CHOICES = (
        ('CAL', 'Accessori calzature'),
        ('ALI', 'Alimentari'),
        ('ARA', 'Arredamenti e accessori'),
        ('AEM', 'Auto e moto'),
        ('CAL', 'Calzature'),
        ('CEG', 'Cartaria e grafica'),
        ('CEP', 'Concerie e pelletterie'),
        ('EDI', 'Edilizia'),
        ('INV', 'Industrie varie'),
        ('IST', 'Istruzione'),
        ('MDC', 'Materiali da costruzione'),
        ('MMC', 'Metalmeccanica'),
        ('SEI', 'Serramenti e infissi'),
        ('STM', 'Strumenti musicali'),
        ('TEI', 'Terziario innovativo'),
        ('TAB', 'Tessile abbigliamento'),
        ('TCP', 'Trasporto cose e persone'),
        ('VAR', 'Vari'),
    )
    ragione_sociale = models.CharField(max_length=200)
    forma_societaria = models.CharField(
        max_length=5, choices=FORMA_SOCIETARIA_CHOICES)
    titolare = models.CharField(max_length=100, blank=True)
    partita_iva = models.CharField(
        max_length=11, verbose_name='Partita IVA', unique=True)
    tipologia = models.CharField(max_length=2, choices=TIPOLOGIA_CHOICES)
    settore = models.CharField(max_length=40, choices=SETTORE_CHOICES)
    prodotto = models.ManyToManyField(Prodotto, blank=True)

    class Meta:
        verbose_name_plural = "clienti"
        verbose_name = "cliente"

    def __unicode__(self):
        return self.ragione_sociale.capitalize()


class Sede(models.Model):
    nome = models.CharField(max_length=100)
    indirizzo = models.CharField(max_length=200, blank=True)
    cliente = models.ForeignKey(Cliente)
    comune = models.ForeignKey(Comune)
    cap = ChainedForeignKey(
        Cap,
        chained_field="comune",
        chained_model_field="comune",
        show_all=False,
        auto_choose=True,
    )

    class Meta:
        verbose_name_plural = "sedi"
        verbose_name = "sede"
        ordering = ['nome']

    def __unicode__(self):
        return self.nome.capitalize() + ", " + self.indirizzo

admin.py (de la demande en cause):

from django.contrib import admin

from .models import Cliente, Prodotto, Sede
from apps.recapito.models import RecapitoCliente


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
    list_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]


admin.site.register(Prodotto)

l'interface d'administration de cette application produit ceci:

Admin interface

raccourci les liens 1 et 2 sont ceux que je dois enlever, étant référé aux colonnes (FKs) inside mes classes en ligne. Les liens de raccourci 3 et 4 doivent être conservés, puisqu'ils renvoient eux-mêmes aux inlines .

7
demandé sur Andy Baker 2014-10-17 17:12:09

3 réponses

je pense que c'est une solution moins macabre que celle avec laquelle tu as fini. Il a travaillé pour moi, de toute façon.

en gros, c'est l'équivalent en ligne de ce que vous avez suggéré de faire avec la méthode get_form de ModelAdmin. Ici, nous outrepassons get_formset dans la classe inline, obtenir le formulaire hors formset, et faire la même chose exacte. Ça a l'air de bien marcher, au moins en 1.9, que j'utilise.

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
9
répondu thegiffman 2016-06-01 04:34:22

pour supprimer l'option" Ajouter un autre", veuillez ajouter la méthode ci-dessous dans la classe admin inline.

def has_add_permission(self, request):
    return False

de même si vous voulez désactiver" supprimer?"option, Ajouter la méthode suivante dans la classe admin inline.

def has_delete_permission(self, request, obj=None):
    return False
14
répondu Sandy 2017-03-16 06:19:04

Après quelques jours intenses, j'ai enfin réussi à trouver un moyen pour y parvenir.

un truc simple comme ce est plus que suffisant pour traiter ce problème dans les sous-classes ModelAdmin (voir ClienteAdmin dans mon code ci-dessus), donc voici la version de classe sans ajouter de capacités pour le champ" Prodotto":

@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

la vraie douleur vient quand il s'agit de classes inline (TabularInline, StackedInline), comme la fonction get_form () ne semble pas être appelée du tout, donc la méthode précédente ne fonctionnera pas.

expliquer toutes mes tentatives précédentes prendrait trop de temps, et je ne suis probablement même pas assez bon avec Django encore pour dire pourquoi ils n'ont pas fonctionné. Donc, allons directement à la solution, qui en fait n'est même pas si compliquée.

je sous-classé django.contrib.admin.widget.RelatedFieldWidgetWrapper widget et outrepasser son rendu méthode, pour qu'il n'ajoute pas l'ancre "add-another" à la sortie. Facile à faire en commentant quelques lignes. Après cela, monkeypatching L'original RelatedFieldWidgetWrapper avec ma propre version ( django.contrib.admin.widget.RelatedFieldWidgetWrapper = NoAddingRelatedFieldWidgetWrapper ) a fait l'affaire.

clairement, pour que cela fonctionne, j'ai dû ajouter la ligne d'importation dans le 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

pour le plaisir de l'achèvement, voici la version finale de la 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

si quelqu'un trouve une meilleure solution, je l'accepterai volontiers à la place de la mienne.

4
répondu Seether 2017-05-23 12:09:56