Pergunta

Sempre que eu estou editando objeto A com uma chave estrangeira para objeto B, um plus opção "adicionar outro" está disponível ao lado das escolhas de objeto B. Como faço para remover essa opção?

Eu configurei um usuário sem direitos para adicionar objeto B. O sinal de adição ainda está disponível, mas quando eu clicar nele, ele diz que "Permissão negada". É feio.

Eu estou usando Django 1.0.2

Foi útil?

Solução

DEPRECADO RESPOSTA

Django, desde então, tornou isso possível.


Você considerou em vez de usar CSS simplesmente não mostrar o botão? Talvez isso é um pouco hacky.

Esta é testado, mas eu estou 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 fazer isso - mídia como uma definição estática

Nota / Edit: A documentação diz que os arquivos serão precedidos com o MEDIA_URL senão em minha experimentação não é. Sua milhagem pode variar.

Se você encontrar este é o caso para você, há uma solução rápida para este ...

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

Outras dicas

(stop upvoting esta resposta errada !!!)

ERRATA : Esta resposta é basicamente errado, e não responder à pergunta de OP. Veja abaixo.

(este é apenas aplicável a formas em linha, campos de chave não estrangeiros como OP perguntou)

solução mais simples, sem corte CSS, nenhuma edição Django codebase:

Adicione isto a sua classe em linha:

max_num=0

Atualizar

Isto não responder à pergunta de OP, e só é útil para ocultar a opção "Adicionar relacionado" botão para formulários em linha, e não chaves estrangeiras, conforme solicitado.

Quando escrevi esta resposta, IIRC a resposta hide aceita ambos, que é por isso que eu fiquei confuso.

Os links a seguir parece fornecer uma solução (embora escondendo usando CSS parece que as coisas mais viáveis ??para fazer, especialmente se a "acrescentar mais" botões de FKs em formas in-line):

Django 1.7 removendo botão Adicionar de forma in-line

Embora a maioria das soluções mencionadas aqui trabalho, não há outra maneira mais limpa de fazê-lo. Provavelmente foi introduzida em uma versão posterior do Django, após as outras soluções foram apresentadas. (Estou usando no momento Django 1.7)

Para remover o "Adicionar outra opção",

class ... #(Your inline class)

    def has_add_permission(self, request):
        return False

Da mesma forma, se você quiser desativar "Excluir?" opção, adicione o seguinte método na classe inline.

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

NB. Trabalha para Django 1.5.2 e possivelmente mais velho. A propriedade can_add_related apareceu cerca de 2 anos atrás.

A melhor maneira que eu encontrei é para substituir a função get_form do seu ModelAdmin. No meu caso eu queria forçar o autor de um post para ser o usuário conectado no momento. Código abaixo com comentários copiosas. O bit realmente importante é a definição 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

A parte interessante de fazer as mudanças aqui em get_form é que author.widget é uma instância de django.contrib.admin.widgets.RelatedFieldWidgetWrapper onde, como se você tentar e fazer mudanças em uma das funções formfield_for_xxxxx, o widget é uma instância do widget forma actual, neste típico ForeignKey caso é um django.forms.widgets.Select.

Olhe para django.contrib.admin.options.py e confira a classe BaseModelAdmin, método formfield_for_dbfield.

Você vai ver isso:

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

Eu acho que sua melhor aposta é criar subclasse de ModelAdmin (que por sua vez é uma subclasse de BaseModelAdmin), basear o seu modelo em que a nova classe, formfield_fo_dbfield override e torná-lo para que ele não vai / ou vai condicionalmente embrulhar o widget em RelatedFieldWidgetWrapper.

Pode-se argumentar que, se você tiver um usuário que não tem direitos para a adição de objetos relacionados, o RelatedFieldWidgetWrapper não devem exibir o link add? Talvez isso é algo que é merecedor de menção no Django trac ?

Eu uso aproxima o seguinte para Form e InlineForm

Django 2.0, Python 3 +

Form

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 Formulário

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

A resposta por @Slipstream mostra como para implementar a solução, viz. , substituindo os atributos para o widget do formfield, mas, na minha opinião, get_form não é o lugar mais lógico para fazer isso.

A resposta por @cethegeek mostra , onde para implementar a solução, viz. numa extensão de formfield_for_dbfield, mas não fornece um exemplo explícito.

uso formfield_for_dbfield

Por quê? Sua docstring sugere que é o gancho designado para mexer com os campos do formulário:

Gancho para especificar a forma instância de campo de uma instância do campo determinado banco de dados.

Ele também permite a (ligeiramente) mais limpo e código mais claro, e, como um bônus, podemos facilmente definir forma adicional Field atributos , tais como valor initial e / ou disabled (exemplo aqui ), adicionando-os ao kwargs (antes de chamar super).

Assim, combinando as duas respostas (assumindo modelos do OP são ModelA e ModelB, eo ForeignKey campo modelo b é nomeado):

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 o campo modelo ForeignKey tem on_delete=models.CASCADE, o atributo can_delete_related é False por padrão, como pode ser visto na fonte para RelatedFieldWidgetWrapper.

Eu estou usando Django 2.x e eu acho que encontrei melhor solução, pelo menos para o meu caso.

O arquivo HTML para o "Salvar e adicionar outro" botão está na your_python_installation\Lib\site-packages\django\contrib\admin\templates\admin\subtmit_line.html.

  1. Copiar esse arquivo html e cole ao seu projeto como assim your_project\templates\admin\submit_line.html.
  2. Abra-o e comentário / apagar o código do botão conforme desejado:

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

Eu sei que este problema já está respondido. Mas talvez alguém no futuro ter um caso semelhante comigo.

Com base na resposta cethegeek Eu fiz esta:

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 Instale Dir) /django/contrib/admin/widgets.py: Comentário tudo entre Linha 239 e Linha 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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top