Pregunta

Siempre que estoy editando el objeto A con una clave externa al objeto B, una opción más "agregar otro" está disponible junto a las opciones de objeto B. ¿Cómo se quita esa opción?

He configurado un usuario sin derechos para agregar objeto B. El signo está todavía disponible, pero cuando hago clic en él, se dice "Permiso denegado". Es feo.

Estoy usando Django 1.0.2

¿Fue útil?

Solución

RESPUESTA DEPRECATED

Django desde entonces ha hecho esto posible.


¿Ha considerado el uso de CSS en lugar de simplemente no mostrar el botón? Tal vez eso es un poco demasiado hacky.

Esto no se ha probado, pero estoy 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 para hacer esto - Medios como una definición estática

Nota / Editar: La documentación dice que los archivos serán precedidas por la MEDIA_URL pero en mi experimentación no lo es. Su kilometraje puede variar.

Si usted encuentra este es el caso para usted, hay una solución rápida para esto ...

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',) }

Otros consejos

(parada Upvoting esta respuesta errónea !!!)

ERRATA : Esta respuesta es básicamente mal, y no responde a la pregunta de OP. Véase más abajo.

  

(esto sólo es aplicable a inline formas, no extranjeros campos clave, tal como solicitó OP)

     

solución más simple, no hay truco CSS, sin edición Django base de código:

     

Agregue esto a su clase en línea:

     

max_num=0

Actualizar

Esto no responde a la pregunta de OP, y sólo es útil para ocultar los "add relacionados con" botón para formularios en línea, y no las claves externas conforme a lo solicitado.

Cuando escribí esta respuesta, la respuesta aceptada IIRC ocultar tanto, es por eso que se confundió.

Los siguientes enlaces parece proporcionar una solución (aunque ocultando el uso de CSS parece lo más factible hacerlo, especialmente si el "añadir otros" botones de FKs en formas en línea):

Django 1.7 quitar el botón Agregar de la forma en línea

Aunque la mayoría de las soluciones mencionadas aquí funciona, hay otra forma más limpia de hacerlo. Probablemente se introdujo en una versión posterior de Django, después se presentaron las otras soluciones. (Actualmente estoy usando Django 1.7)

Para eliminar la "otra Añadir" opción,

class ... #(Your inline class)

    def has_add_permission(self, request):
        return False

Del mismo modo, si desea desactivar "Eliminar?" opción, añadir el siguiente método en la clase en línea.

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

N.B. Obras para DJango 1.5.2 y posiblemente más. La propiedad can_add_related apareció alrededor de hace 2 años.

La mejor manera que he encontrado es para anular la función get_form de su ModelAdmin. En mi caso quería forzar el autor de la entrada para ser el usuario conectado en ese momento. Código de abajo con comentarios copiosas. La parte realmente importante es el establecimiento 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 parte interesante de hacer los cambios aquí en get_form es que author.widget es una instancia de django.contrib.admin.widgets.RelatedFieldWidgetWrapper donde como si usted trata de hacer cambios en una de las funciones formfield_for_xxxxx, el widget es una instancia del widget real de la forma, en esta típica ForeignKey caso se trata de un django.forms.widgets.Select.

Mira django.contrib.admin.options.py y echa un vistazo a la clase BaseModelAdmin, método formfield_for_dbfield.

Usted verá esto:

# 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)

Creo que lo mejor es crear subclase de ModelAdmin (que a su vez es una subclase de BaseModelAdmin), la base de su modelo en que la nueva clase, formfield_fo_dbfield override y hacerlo de modo que no lo hará / o condicionalmente se envuelva el widget en RelatedFieldWidgetWrapper.

Se podría argumentar que si usted tiene un usuario que no tiene derechos a la adición de objetos relacionados, la RelatedFieldWidgetWrapper no debe mostrar el enlace añadir? Tal vez esto es algo que es digno de mención en Django trac ?

Yo uso los siguientes enfoques para Formulario y InlineForm

Django 2.0, Python 3 +

Formulario

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  

Formulario en línea

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 respuesta de @Slipstream espectáculos ¿Cómo para implementar la solución, a saber. anulando los atributos para el widget del formfield, pero, en mi opinión, get_form no es el lugar más lógico para hacer esto.

La respuesta de @cethegeek visualizan donde para implementar la solución, a saber. en una extensión de formfield_for_dbfield, pero no proporciona un ejemplo explícito.

¿Por qué utilizar formfield_for_dbfield? Su docstring sugiere que es el gancho designado para jugar con campos de formulario:

  

Enganche para especificar la instancia de formulario de campo para una instancia de base de datos de campo dado.

También permite código (ligeramente) más limpia y más clara, y, como un bono, podemos ajustar fácilmente forma adicional Field atributos , tales como el valor initial y / o disabled (ejemplo aquí ), mediante su inclusión en la kwargs (antes de llamar super).

Por lo tanto, la combinación de las dos respuestas (suponiendo modelos de la OP son ModelA y ModelB, y el campo de modelo ForeignKey lleva el nombre 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: Si el campo modelo ForeignKey tiene on_delete=models.CASCADE, el atributo can_delete_related se False por defecto, como puede verse en el fuente de RelatedFieldWidgetWrapper.

Estoy usando Django 2.x y creo que he encontrado la mejor solución, al menos para mi caso.

El archivo HTML en la opción "Guardar y agregar otro" botón se encuentra en your_python_installation\Lib\site-packages\django\contrib\admin\templates\admin\subtmit_line.html.

  1. Copiar ese archivo HTML y pegar a su proyecto como tal your_project\templates\admin\submit_line.html.
  2. abrirlo y comentar / borrar el código del botón según se desee:

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

Sé que este problema ya está contestada. Pero tal vez alguien en el futuro tiene un caso similar conmigo.

En base a la respuesta cethegeek hice esto:

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: Comentario todo entre la línea 239 y línea 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')))
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top