Login

Dynamically add inlines

Author:
MasonM
Posted:
June 24, 2009
Language:
JavaScript
Version:
Not specified
Score:
7 (after 7 ratings)

These functions use JQuery to dynamically add new entries for stacked or tabular inlines on a change form. To enable it, change the parent model to include this Javascript as well as JQuery. Here's an example:

class MeetingAdmin(admin.ModelAdmin):
    inlines = [MeetingDonationInline, MeetingExtraInline]

    class Media:
        js = ["/media/jquery-1.3.2.min.js",
              "/media/dynamic_inlines.js"]
 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. Django Collapsed Stacked Inlines by applecat 3 years ago
  2. Django Collapsed Stacked Inlines by mkarajohn 5 years, 2 months ago
  3. Dynamically adding forms to a formset. OOP version. by halfnibble 10 years, 10 months ago
  4. Convert multiple select for m2m to multiple checkboxes in django admin form by abidibo 12 years, 11 months ago
  5. Django admin inline ordering - javascript only implementation by ojhilt 13 years, 3 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('<div class="add-item">Add another</div>');

(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.

#

Please login first before commenting.