5

I have a Client model, which includes a field for a client API key.

When adding a new client in Django Admin, I'd like to have a button next to the API field to generate a new key (i have the method for this). The field will then be updated with the key once generated.

How can I add this button next to the field? Should I use a custom widget?

2
  • Judging from the different tags you add to this question, there is too little information to answer it seriously.
    – Private
    Commented Aug 14, 2013 at 10:46
  • You can ovveride djangos admin templates and use your own. This article may help to get into the right direction: code.djangoproject.com/wiki/ExtendingAdminTemplates
    – Jingo
    Commented Aug 14, 2013 at 10:52

1 Answer 1

11

In my case I am making an API call with a button I create so I'll throw in how I did that too. Ofcourse your button can do whatever you like.

First, in your model create a function that will output your button. I will use my example, i.e. models.py:

class YourModel(models.Model):

    ....

    def admin_unit_details(self):  # Button for admin to get to API
        return format_html(u'<a href="#" onclick="return false;" class="button" '
                           u'id="id_admin_unit_selected">Unit Details</a>')
    admin_unit_details.allow_tags = True
    admin_unit_details.short_description = "Unit Details"

I then added the field as readonly and added it to the fieldsets, note you can only have either fields or fieldsets defined on the model admin. I aslo added media to overwrite some css and also added the js for where the ajax call will be made, admin.py:

class YourModelAdmin(admin.ModelAdmin):

    form = YourModelForm
    list_display = ('id', 'agent', 'project', 'completed_date', 'selected_unit', 'is_accepted',
                    'get_lock_for_admin', 'status')

    fields = ('agent', 'project', 'completed_date', 'selected_unit', 'is_accepted',
                    'lock', 'status')

    readonly_fields = ('admin_unit_details', )

    ...

    class Media:
        js = ('admin_custom/js/myjs.js',)  # in static
        css = {'all': ('admin_custom/css/mycss.css', )}

I also wanted to note that I passed the API address and header through the Form, but you can use the right header/password in the code. I just keep mine all in one place (settings.py), forms.py (optional):

from settings import API_ADDRESS, API_HEADER
class MyModelForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        super(WorksheetForm, self).__init__(*args, **kwargs)

        self.fields['selected_unit'].widget = forms.Select(choices=get_worksheet_unit_choice_list(self.instance.id),
                                                           attrs={'api_address': API_ADDRESS, 'api_header': API_HEADER})

    ....

Lastly here is a look at my js, as referenced by my admin Media class, it is in admin_custom/js/myjs.js:

This is similar to adding an admin image, see here. Also search for allow_tags attribute in this django doc, it shows a good example.

// Make sure jQuery (django admin) is available, use admin jQuery instance
if (typeof jQuery === 'undefined') {
    var jQuery = django.jQuery;
}

var unit_information = {};

jQuery( document ).ready(function() {

    jQuery('#id_admin_unit_selected').click( function() {

        //get the data from id_selected_unit, from the api_header api_address attributes
        var unit_select = jQuery('#id_selected_unit');
        var header = unit_select.attr('api_header');
        var address = unit_select.attr('api_address');
        var selected_unit = unit_select.val();

        if (header && address && selected_unit){
            var unit_address = address + '/units/' + selected_unit
            get_unit(header, unit_address)
        }
        else{
            // if can't connect to api, so hide
            jQuery('.field-admin_unit_details').hide();
        }

    });

});


function get_unit(header, address){

    jQuery.ajax
    ({
        type: "GET",
        url: address,
        dataType: 'json',
        headers: {
        "Authorization": header
        },
        success: function (result) {
            //TODO: output this in a modal & style
            unit_information = JSON.stringify(result);
            alert(unit_information)
        },
        error: function(xhr, textStatus, errorThrown) {
            alert("Please report this error: "+errorThrown+xhr.status+xhr.responseText);
        }
    });

}

This outputs it in an alert, you can also log it to the console or define your own modal / style for it.

Hope this helps, Cheers!

2
  • 1
    Note: The button MUST be listed as a read only field or it will throw an error. stackoverflow.com/questions/22682516/… Commented Jul 6, 2016 at 13:05
  • 1
    I simplified this (no additional JS & CSS) and it worked for me. I moved the function that outputs the button right into the admin file so as not to clutter my model. Also, allow_tags is no longer needed! Commented Dec 4, 2020 at 5:33

Not the answer you're looking for? Browse other questions tagged or ask your own question.