Rimuovi “aggiungere un altro” a schermo amministrazione Django
-
19-09-2019 - |
Domanda
Ogni volta che sto modificando l'oggetto A con una chiave esterna di opporsi B, l'opzione più "aggiungere un altro" è disponibile accanto alle scelte di oggetto B. Come faccio a rimuovere tale opzione?
Ho configurato un utente senza diritti per aggiungere l'oggetto B. Il segno più è ancora disponibile, ma quando lo scatto sopra, si dice "Permesso negato". E 'brutto.
sto usando Django 1.0.2
Soluzione
RISPOSTA DEPRECATO
Django da allora ha reso possibile tutto questo.
Avete considerato invece utilizzare i CSS per semplicemente non visualizzare il pulsante? Forse è un po 'troppo hacky.
Questa non è testato, ma sto pensando ...
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 per fare questo - media come definizione statica
Nota / Edit: La documentazione dice che i file verranno prefissati con la MEDIA_URL ma nella mia sperimentazione non lo è. La vostra situazione potrebbe essere diversa.
Se trovate questo è il caso per voi, c'è una soluzione rapida per questo ...
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',) }
Altri suggerimenti
(fermata upvoting questa risposta sbagliata !!!)
ERRATA : Questa risposta è fondamentalmente sbagliata, e non risponde alla domanda di OP. Vedi sotto.
(questo è applicabile solo a inline forme, non campi di chiave esterna come chiesto OP)
soluzione più semplice, senza trucco CSS, nessuna modifica Django codebase:
Aggiungi questo alla classe Inline:
max_num=0
Aggiorna
Questo non risponde alla domanda del PO, ed è utile per nascondere i "add correlate" pulsante per le forme in linea, e non solo le chiavi esterne come richiesto.
Quando ho scritto questa risposta, IIRC la risposta accettata nascondere sia, che è il motivo per cui mi sono confuso.
I seguenti collegamenti sembra fornire una soluzione (anche se nascosto usando i CSS sembra le cose più fattibili da fare, soprattutto se il "aggiungere un altro" pulsanti di FKS in moduli inline):
Anche se la maggior parte delle soluzioni di cui qui funziona, c'è un altro modo più pulito di farlo. Probabilmente è stato introdotto in una versione successiva di Django, dopo che le altre soluzioni sono state presentate. (Sto attualmente utilizzando Django 1.7)
Per rimuovere il "Aggiungere un'altra opzione",
class ... #(Your inline class)
def has_add_permission(self, request):
return False
Allo stesso modo, se si desidera disattivare "Delete?" opzione, aggiungere il seguente metodo della classe Inline.
def has_delete_permission(self, request, obj=None):
return False
NB. Opere per Django 1.5.2 e forse più anziani. La proprietà can_add_related
apparso circa 2 anni fa.
Il modo migliore che ho trovato è quello di ignorare la funzione get_form del ModelAdmin. Nel mio caso ho voluto forzare l'autore di un post per essere l'utente attualmente connesso. Codice qui sotto con i commenti copiose. Il bit veramente importante è l'impostazione di 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 parte interessante di apportare le modifiche qui a get_form
è che author.widget
è un'istanza di django.contrib.admin.widgets.RelatedFieldWidgetWrapper
dove come se si cerca di apportare modifiche in una delle funzioni formfield_for_xxxxx
, il widget è un'istanza del widget attuale forma, in questo tipico ForeignKey caso si tratta di un django.forms.widgets.Select
.
Guarda django.contrib.admin.options.py
e controllare la classe BaseModelAdmin
, metodo formfield_for_dbfield
.
Si vedrà questo:
# 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)
Credo che la cosa migliore è creare sottoclasse di ModelAdmin
(che a sua volta è una sottoclasse di BaseModelAdmin
), basare il proprio modello su quella nuova classe, formfield_fo_dbfield
override e fare in modo che non sarà / o che condizionalmente avvolgere il widget in RelatedFieldWidgetWrapper
.
Si potrebbe sostenere che se si dispone di un utente che non dispone dei diritti per l'aggiunta di oggetti correlati, il RelatedFieldWidgetWrapper
non dovrebbe visualizzare il link aggiungi? Forse questo è qualcosa che è degno di menzione in Django trac ?
Io uso i seguenti approcci per la Modulo e InlineForm
Django 2.0, Python 3 +
Modulo
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
Inline Modulo
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 risposta da @Slipstream spettacoli come per implementare la soluzione, vale a dire. sovrascrivendo gli attributi per il widget del formfield, ma, a mio parere, get_form
non è il posto più logico per fare questo.
La risposta da @cethegeek spettacoli , dove per implementare la soluzione, vale a dire. un prolungamento della formfield_for_dbfield
, ma non fornisce un esempio esplicito.
Perché usare formfield_for_dbfield
? Il suo docstring suggerisce che è il gancio designata per scherzi con campi modulo:
Agganciare per specificare l'istanza modulo campo per una determinata istanza di database Field.
Ciò consente inoltre di (leggermente) codice più pulito e più chiara, e, come bonus, possiamo facilmente impostare ulteriore forma Field
attributi , come valore initial
e / o disabled
(esempio qui ), aggiungendoli al kwargs
(prima di chiamare super
).
Quindi, combinando le due risposte (assumendo modelli del OP sono ModelA
e ModelB
, e il campo Modello ForeignKey
prende il nome 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)
Nota: se il campo Modello ForeignKey
ha on_delete=models.CASCADE
, l'attributo can_delete_related
è False
di default, come si può vedere nella fonte per RelatedFieldWidgetWrapper
.
Sto usando Django 2.xe Credo di avere trovato la soluzione migliore, almeno per il mio caso.
Il file HTML per la "Salva e aggiungere un altro" tasto è in your_python_installation\Lib\site-packages\django\contrib\admin\templates\admin\subtmit_line.html
.
- Copiare il file html e incollarlo al progetto in questo modo
your_project\templates\admin\submit_line.html
. - aprirlo e commentare / cancellare il codice del pulsante, se lo desideri:
{#{% if show_save_and_add_another %}<input type="submit" value="{% trans 'Save and add another' %}" name="_addanother" />{% endif %}#}
So che questo problema è già una risposta. Ma forse qualcuno in futuro avere un caso simile con me.
In base a risposta cethegeek ho fatto questo:
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 Install Dir) /django/contrib/admin/widgets.py: Commento tutto tra Linea 239 & Line 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')))