TAGS :Viewed: 12 - Published at: a few seconds ago

[ Remove "add another" in Django admin screen ]

Whenever I'm editing object A with a foreign key to object B, a plus option "add another" is available next to the choices of object B. How do I remove that option?

I configured a user without rights to add object B. The plus sign is still available, but when I click on it, it says "Permission denied". It's ugly.

I'm using Django 1.0.2

Answer 1


(stop upvoting this wrong answer!!!)

ERRATA : This answer is basically wrong, and does not answer OP's question. See below.

(this is only applicable to inline forms, not foreign key fields as OP asked)

Simpler solution, no CSS hack, no editing Django codebase :

Add this to your Inline class :

max_num=0

UPDATE

This does not answer OP's question, and is only useful to hide the "add related" button for inline forms, and not foreign keys as requested.

When I wrote this answer, IIRC the accepted answer hide both, which is why I got confused.

The following links seems to provide a solution (though hiding using CSS seems the most feasible things to do, especially if the "add another" buttons of FKs in inline forms) :

Django 1.7 removing Add button from inline form

Answer 2


N.B. Works for DJango 1.5.2 and possibly older. The can_add_related property appeared around 2 years ago.

The best way I've found is to override your ModelAdmin's get_form function. In my case I wanted to force the author of a post to be the currently logged in user. Code below with copious comments. The really important bit is the setting of 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

The interesting part of making the changes here in get_form is that author.widget is an instance of django.contrib.admin.widgets.RelatedFieldWidgetWrapper where as if you try and make changes in one of the formfield_for_xxxxx functions, the widget is an instance of the actual form widget, in this typical ForeignKey case it's a django.forms.widgets.Select.

Answer 3


Though most of the solutions mentioned here work, there is another cleaner way of doing it. Probably it was introduced in a later version of Django, after the other solutions were presented. (I'm presently using Django 1.7)

To remove the "Add another" option,

class ... #(Your inline class)

    def has_add_permission(self, request):
        return False

Similarly if you want to disable "Delete?" option, add the following method in Inline class.

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

Answer 4


Look at django.contrib.admin.options.py and check out the BaseModelAdmin class, formfield_for_dbfield method.

You will see this:

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

I think your best bet is create subclass of ModelAdmin (which in turn is a subclass of BaseModelAdmin), base your model on that new class, override formfield_fo_dbfield and make it so that it won't/or will conditionally wrap the widget in RelatedFieldWidgetWrapper.

One could argue that if you have a user that doesn't have rights to adding related objects, the RelatedFieldWidgetWrapper should not display the add link? Maybe this is something that is deserving of mention in Django trac?

Answer 5


DEPRECATED ANSWER

Django has since made this possible. See below. http://stackoverflow.com/a/13159832/111375


Have you considered instead using CSS to simply not show the button? Maybe that's a little too hacky.

This is untested, but I'm thinking...

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 for doing this -- Media as a static definition

Note/Edit: The documentation says the files will be prepended with the MEDIA_URL but in my experimentation it isn't. Your mileage may vary.

If you find this is the case for you, there's a quick fix for this...

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