Dynamically add inlines

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/* Following functions based off code written by Arne Brodowski
http://www.arnebrodowski.de/blog/507-Add-and-remove-Django-Admin-Inlines-with-JavaScript.html
*/
function increment_form_ids(el, to, name) {
    var from = to-1
    $(':input', $(el)).each(function(i,e){
        var old_name = $(e).attr('name')
        var old_id = $(e).attr('id')
        $(e).attr('name', old_name.replace(from, to))
        $(e).attr('id', old_id.replace(from, to))
        $(e).val('')
    })
}

function add_inline_form(name) {
    var first = $('#id_'+name+'-0-id').parents('.inline-related')
    // check to see if this is a stacked or tabular inline
    if (first.hasClass("tabular")) {
        var field_table = first.parent().find('table > tbody')
        var count = field_table.children().length
        var copy = $('tr:last', field_table).clone(true)
        copy.removeClass("row1 row2")
        copy.addClass("row"+((count % 2) == 0 ? 1 : 2))
        field_table.append(copy)
        increment_form_ids($('tr:last', field_table), count, name)
    }
    else {
        var last = $(first).parent().children('.last-related')
        var copy = $(last).clone(true)
        var count = $(first).parent().children('.inline-related').length
        $(last).removeClass('last-related')
        var header = $('h3', copy)
        header.html(header.html().replace("#"+count, "#"+(count+1)))
        $(last).after(copy)
        increment_form_ids($(first).parents('.inline-group').children('.last-related'), count, name)
    }
    $('input#id_'+name+'-TOTAL_FORMS').val(count+1)
    return false;
}

// Add all the "Add Another" links to the bottom of each inline group
$(function() {
    var html_template = '<ul class="tools">'+
        '<li>'+
            '<a class="add" href="#" onclick="return add_inline_form(\'{{prefix}}\')">'+
            'Add another</a>'+
        '</li>'+
    '</ul>'
    $('.inline-group').each(function(i) {
        //prefix is in the name of the input fields before the "-"
        var prefix = $("input[type='hidden']", this).attr("name").split("-")[0]
        $(this).append(html_template.replace("{{prefix}}", prefix))
    })
})

More like this

  1. Collapsed stacked inlines by Aneon 4 years, 11 months ago
  2. Dynamic tabular inlines with optional drag-n-drop sorting by Aneon 4 years, 11 months ago
  3. GeoDjango maps in admin TabularInlines by alanB 3 years, 6 months ago
  4. Django admin inline ordering - javascript only implementation by ojhilt 1 year, 4 months ago
  5. Adding buttons to submit line in a Admin page by marinho 5 years, 11 months ago

Comments

simon (on June 25, 2009):

An improvement would be to add the "add a new inline" button using JavaScript . This would give you two advantages: first, there's no need to alter the template itself (you can just use Media.js to add the script). Secondly, it means that users without JavaScript won't see a link that doesn't do anything.

#

andybak (on June 25, 2009):

I did something similar. The bit I disagree with here is the 'copy the appropriate file ... into your project's template directory' bit. That makes upgrading Django a headache.

Instead I simply created the 'Add' link dynamically: $('.inline-group').append('[HTML_REMOVED]Add another[HTML_REMOVED]');

(Jquery used in that example)

#

MasonM (on June 25, 2009):

An improvement would be to add the "add a new inline" button using JavaScript

Good idea. The reason I didn't do this was because I had already extended the stacked and tabular inline templates in my project for other purposes. I doubt many others are in that situation, so I went ahead and added that feature.

#

dimitri (on June 25, 2009):

If there is an inline with max_num=1, the javascript function should not be there, {% ifnotequal inline_admin_formset.formset.max_num 1 %} in your template code prevents this from happening.

#

gregb (on August 30, 2009):

Just a tip, it's best practice to always use semicolons in javascript. The fact that they aren't enforced is definitely a bug and not a feature of the language. (One example of why: if this script was minified it wouldn't work because minification removes newlines)

Regardless, very handy script. Cheers.

#

smcoll (on December 14, 2009):

i noticed that js for prepopulated fields (like slugs) doesn't work with the newly created inlines, since the necessary script (from django/contrib/admin/templates/admin/prepopulated_fields_js.html) isn't also created. i don't know what the solution to this is, since in some cases (extra=0 for inlines with slugfields) there wouldn't even be script to duplicate.

#

(Forgotten your password?)