Question

Chaque fois que je suis d'édition objet A avec une clé étrangère à l'objet B, une option plus « ajouter une autre » est disponible à côté des choix de l'objet B. Comment puis-je supprimer cette option?

Je configuré un utilisateur sans droits pour ajouter des objets B. Le signe plus est toujours disponible, mais quand je clique dessus, il dit « Autorisation refusée ». Il est laid.

J'utilise Django 1.0.2

Était-ce utile?

La solution

RÉPONSE DECONSEILLE

Django a depuis rendu cela possible.


Avez-vous envisagé au lieu d'utiliser les CSS pour montrer tout simplement pas le bouton? Peut-être est un peu trop aki.

Ceci est non testé, mais je pense ...

no-addanother-button.css

#_addanother { display: none }

admin.py

class YourAdmin(admin.ModelAdmin):
    # ...
    class Media:
        # edit this path to wherever
        css = { 'all' : ('css/no-addanother-button.css',) }

Django Doc pour ce faire - médias comme une définition statique

Note / Edit: La documentation indique que les fichiers seront ajoutés avec le MEDIA_URL mais dans mon expérimentation il n'est pas. Votre kilométrage peut varier.

Si vous trouvez cela est le cas pour vous, il y a une solution rapide pour cette ...

class YourAdmin(admin.ModelAdmin):
    # ...
    class Media:
        from django.conf import settings
        media_url = getattr(settings, 'MEDIA_URL', '/media/')
        # edit this path to wherever
        css = { 'all' : (media_url+'css/no-addanother-button.css',) }

Autres conseils

(arrêt upvoting cette mauvaise réponse !!!)

ERRATA : Cette réponse est fondamentalement faux, et ne répond pas à la question de l'OP. Voir ci-dessous.

  

(ce qui est uniquement applicable à inline formes, pas de champs clés étrangères OP a demandé)

     

solution plus simple, pas de hack CSS, pas d'édition Django codebase:

     

Ajoutez ceci à votre classe Inline:

     

max_num=0

UPDATE

Cela ne répond pas à la question de l'OP, et est utile pour cacher le bouton « ajouter liés » pour formulaires en ligne, et non les clés étrangères comme l'a demandé.

Quand j'ai écrit cette réponse, IIRC la réponse acceptée cacher à la fois, ce qui est la raison pour laquelle je me suis embrouillé.

Les liens suivants semblent fournir une solution (mais cachant en utilisant CSS semble les choses à faire plus réalisables, surtout si le « ajouter un autre » boutons de FKs sous forme de ligne):

Django 1.7 retirer le bouton Ajouter de la forme en ligne

Bien que la plupart des solutions mentionnées ici fonctionnent, il y a une autre façon plus propre de le faire. Probablement il a été introduit dans une version ultérieure de Django, après que les autres solutions ont été présentées. (J'utilise actuellement Django 1.7)

Pour supprimer l'option "Ajouter une autre",

class ... #(Your inline class)

    def has_add_permission(self, request):
        return False

De même, si vous voulez désactiver « Supprimer? » l'option, ajoutez la méthode suivante en classe ligne.

    def has_delete_permission(self, request, obj=None):
        return False

N.B.. Travaux pour django 1.5.2 et peut-être plus. La propriété can_add_related apparu il y a environ 2 ans.

La meilleure façon que j'ai trouvé est de remplacer la fonction get_form de votre ModelAdmin. Dans mon cas, je voulais forcer l'auteur d'un poste pour être connecté comme utilisateur actuellement. Code ci-dessous avec des commentaires copieux. Le bit vraiment important est le réglage de widget.can_add_related:

def get_form(self,request, obj=None, **kwargs):
    # get base form object    
    form = super(BlogPostAdmin,self).get_form(request, obj, **kwargs)

    # get the foreign key field I want to restrict
    author = form.base_fields["author"]

    # remove the green + by setting can_add_related to False on the widget
    author.widget.can_add_related = False

    # restrict queryset for field to just the current user
    author.queryset = User.objects.filter(pk=request.user.pk)

    # set the initial value of the field to current user. Redundant as there will
    # only be one option anyway.
    author.initial = request.user.pk

    # set the field's empty_label to None to remove the "------" null 
    # field from the select. 
    author.empty_label = None

    # return our now modified form.
    return form

La partie intéressante de faire les changements ici get_form est que author.widget est une instance de django.contrib.admin.widgets.RelatedFieldWidgetWrapper où comme si vous essayez de faire des changements dans l'une des fonctions de formfield_for_xxxxx, le widget est une instance du widget forme réelle, dans ce type ForeignKey cas, il est un django.forms.widgets.Select.

Regardez django.contrib.admin.options.py et consultez la classe BaseModelAdmin, méthode formfield_for_dbfield.

Vous verrez ceci:

# For non-raw_id fields, wrap the widget with a wrapper that adds
# extra HTML -- the "add other" interface -- to the end of the
# rendered output. formfield can be None if it came from a
# OneToOneField with parent_link=True or a M2M intermediary.
if formfield and db_field.name not in self.raw_id_fields:
    formfield.widget = widgets.RelatedFieldWidgetWrapper(formfield.widget, db_field.rel, self.admin_site)

Je pense que le mieux est de créer de la sous-classe ModelAdmin (ce qui est une sous-classe de BaseModelAdmin), la base de votre modèle sur cette nouvelle classe, override formfield_fo_dbfield et faire en sorte que ce ne sera pas / ou sera conditionnellement envelopper le widget en RelatedFieldWidgetWrapper.

On pourrait dire que si vous avez un utilisateur qui ne dispose pas de droits à l'ajout d'objets liés, l'RelatedFieldWidgetWrapper ne doit pas afficher le lien ajouter? Peut-être cela est quelque chose qui mérite d'être mentionné dans Django trac?

J'utilise les approches suivantes pour Formulaire et InlineForm

Django 2.0, Python 3 +

Formulaire

class MyModelAdmin(admin.ModelAdmin):
    #...
    def get_form(self,request, obj=None, **kwargs):

        form = super().get_form(request, obj, **kwargs)
        user = form.base_fields["user"]

        user.widget.can_add_related = False
        user.widget.can_delete_related = False
        user.widget.can_change_related = False

        return form  

Formulaire Inline

class MyModelInline(admin.TabularInline):
    #...
    def get_formset(self, request, obj=None, **kwargs):

        formset = super().get_formset(request, obj, **kwargs)
        user = formset.form.base_fields['user']

        user.widget.can_add_related = False
        user.widget.can_delete_related = False
        user.widget.can_change_related = False

        return formset

La réponse par @Slipstream montre comment pour mettre en œuvre la solution, à savoir. en remplaçant les attributs pour le widget formfield, mais, à mon avis, get_form n'est pas l'endroit le plus logique de le faire.

La réponse par @cethegeek pour mettre en œuvre la solution, à savoir. dans une extension de formfield_for_dbfield, mais ne fournit pas un exemple explicite.

Pourquoi utiliser formfield_for_dbfield? Son docstring suggère que il est désigné le crochet pour jouer avec des champs de formulaire:

  

Crochet pour spécifier la forme par exemple de champ pour une base de données instance de champ donné.

Il permet aussi (un peu) un code plus propre et plus claire, et, en prime, nous pouvons facilement mettre en forme Field supplémentaire attributs, tels que la valeur de initial et / ou disabled (par exemple ici ), en les ajoutant à la kwargs (avant d'appeler super).

Ainsi, en combinant les deux réponses (en supposant les modèles de l'OP sont ModelA et ModelB, et le champ modèle ForeignKey est nommé b):

class ModelAAdmin(admin.ModelAdmin):
    def formfield_for_dbfield(self, db_field, request, **kwargs):
        # optionally set Field attributes here, by adding them to kwargs
        formfield = super().formfield_for_dbfield(db_field, request, **kwargs)
        if db_field.name == 'b':
            formfield.widget.can_add_related = False
            formfield.widget.can_change_related = False
            formfield.widget.can_delete_related = False
        return formfield

# Don't forget to register...
admin.site.register(ModelA, ModelAAdmin)

NOTE: Si le champ modèle ForeignKey a on_delete=models.CASCADE, l'attribut can_delete_related est False par défaut, comme on peut le voir dans la section Source RelatedFieldWidgetWrapper.

J'utilise 2.x Django et je pense que j'ai trouvé la meilleure solution, au moins pour mon cas.

Le fichier HTML « Enregistrer et ajouter un autre » bouton est sur your_python_installation\Lib\site-packages\django\contrib\admin\templates\admin\subtmit_line.html.

  1. Copier ce fichier html et le coller à votre projet comme si your_project\templates\admin\submit_line.html.
  2. Ouvrir et commenter / supprimer le code du bouton comme vous le souhaitez:

{#{% if show_save_and_add_another %}<input type="submit" value="{% trans 'Save and add another' %}" name="_addanother" />{% endif %}#}

Je sais que ce problème est déjà répondu. Mais peut-être quelqu'un à l'avenir avoir une affaire similaire avec moi.

Sur la base de réponse cethegeek je fait ceci:

class SomeAdmin(admin.ModelAdmin):
    form = SomeForm

    def formfield_for_dbfield(self, db_field, **kwargs):
        formfield = super(SomeAdmin, self).formfield_for_dbfield(db_field, **kwargs)
        if db_field.name == 'some_m2m_field':
            request = kwargs.pop("request", None)
            formfield = self.formfield_for_manytomany(db_field, request, **kwargs)  # for foreignkey: .formfield_for_foreignkey
            wrapper_kwargs = {'can_add_related': False, 'can_change_related': False, 'can_delete_related': False}
            formfield.widget = admin.widgets.RelatedFieldWidgetWrapper(
                formfield.widget, db_field.remote_field, self.admin_site, **wrapper_kwargs
            )
        return formfield

django.contrib.admin.widgets.py

(Django Répertoire d'installation) /django/contrib/admin/widgets.py: Commentaire tout entre la ligne 239 et ligne 244:

 if rel_to in self.admin_site._registry: # If the related object has an admin interface:
        # TODO: "id_" is hard-coded here. This should instead use the correct
        # API to determine the ID dynamically.
        output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \
            (related_url, name))
        output.append(u'<img src="%simg/admin/icon_addlink.gif" width="10" height="10" alt="%s"/></a>' % (settings.ADMIN_MEDIA_PREFIX, _('Add Another')))
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top