Orderable inlines using drag and drop with jQuery UI

 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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# models.py

from django.db import models

class Menu(models.Model):
    name = models.CharField(max_length = 100)
    
    def __unicode__(self):
        return self.name

class Item(models.Model):
    menu = models.ForeignKey(Menu)
    name = models.CharField(max_length = 100)
    url = models.CharField(max_length = 100)
    order = models.IntegerField(blank = True, null = True)
    
    def __unicode__(self):
        return self.name
    
    class Meta:
        ordering = ('order',)

# admin.py

from django.contrib import admin
from django import forms

from models import Menu, Item

class MenuForm(forms.ModelForm):
    model = Menu
    class Media:
        js = (
            '/static/js/jquery-latest.js',
            '/static/js/ui.base.js',
            '/static/js/ui.sortable.js',
            '/static/js/menu-sort.js',
        )

class ItemInline(admin.StackedInline):
    model = Item

admin.site.register(Menu,
    inlines = [ItemInline],
    form = MenuForm,
)

"""
/* menu-sort.js */

jQuery(function($) {
    $('div.inline-group').sortable({
        /*containment: 'parent',
        zindex: 10, */
        items: 'div.inline-related',
        handle: 'h3:first',
        update: function() {
            $(this).find('div.inline-related').each(function(i) {
                if ($(this).find('input[id$=name]').val()) {
                    $(this).find('input[id$=order]').val(i+1);
                }
            });
        }
    });
    $('div.inline-related h3').css('cursor', 'move');
    $('div.inline-related').find('input[id$=order]').parent('div').hide();
});
"""

Comments

richardh (on September 13, 2008):

Great presentation at PyCon UK 2008 this morning Simon. I have been wondering how do this for a year!

#

exogen (on September 14, 2008):

Nice! Just a note for anyone who was confused like I was - the JavaScript only works on StackedInlines, not TabularInlines.

#

mikko (on September 18, 2008):

Nice and simple! I have implemented reordering in my project sorl-curetor where you can also reorder in groups with respect to another field.

#

patrickk (on October 23, 2008):

ordering is lost if there“s an error somewhere in the form.

#

trey (on December 19, 2008):

If you change line 63 (menu-sort.js) to

$(this).find('div.inline-related').not('div.inline-related:last').each(function(i) {

and then remove the if statement, It's a little less brittle in terms of field names (as it is here it requires a name field in the inline model.

#

andybak (on January 5, 2009):

This should work for TabularInlines. You might want to tweak my choice of handle and you'll have to change the eq(5) in the last line depending where in the field list you have placed 'order':

jQuery(function($) {
    $('.module table').sortable({
        items: 'tr',
        handle: 'td.title',
        update: function() {
            $(this).find('tr').each(function(i) {
                if ($(this).find('input[class$=vTextField]').val()) {
                    $(this).find('input[id$=order]').val(i+1);
                }
            });
        }
    });
    $('.module td.title').css('cursor', 'move');
    $('.module table').find('input[id$=order]').parent('td').hide();
    $('.module table thead th:eq(5)').hide();
});

#

leplatrem (on June 26, 2009):

At first, it looked pretty easy. But, I realized the menu-sort.js was made for an older version of Django.

With django1.02, inline items in the automatic admin look like:

<div class="inline-group">
<h2>Items</h2>
<div class="inline-related">
  <h3><b>Item:</b>Coffee</h3>
  <fieldset class="module aligned">
  <div class="form-row name">...</div>
  <div class="form-row order">...</div>
</div>
</div>

Does anyone already worked on updating menu-sort.js ?

Thank you all

#

southern_sun (on June 26, 2009):

The following script works with TabularInline in latest django release:

    jQuery(function($) {
        $('div.inline-group').sortable({
            items: 'tr.has_original',
            handle: 'td',
            update: function() {
                $(this).find('tr.has_original').each(function(i) {
                    $(this).find('input[name$=order]').val(i+1);
                    $(this).removeClass('row1').removeClass('row2');
                    $(this).addClass('row'+((i%2)+1));
                });
            }
        });
        $('tr.has_original').css('cursor', 'move');
    });

#

leplatrem (on June 26, 2009):

I managed to adapt it. Your critics about this piece of javascript are warmly welcomed!

$(function() {
    $("div.inline-group").sortable({ 
        //axis: 'y',
        placeholder: 'ui-state-highlight', 
        forcePlaceholderSize: 'true', 
        items: 'div.inline-related', 
        update: function() {
            $(this).find('div.inline-related').each(function(i) {
                if ($(this).find('.vTextField').val()) {
                    $(this).find('input[id$=order]').val(i+1);
                }
        });
        }
    });
    $("div.inline-group").disableSelection();

});

jQuery(document).ready(function($){
    $(this).find('input[id$=order]').parent('div').parent('div').hide()
});

#

(Forgotten your password?)

You may use Markdown syntax here, but raw HTML will be removed.