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();
});
"""

More like this

  1. Drag and drop ordering of admin list elements using jQuery UI by johj 2 years, 11 months ago
  2. Drag and drop admin list items by chrsgrrtt 2 years, 9 months ago
  3. Dynamic tabular inlines with optional drag-n-drop sorting by Aneon 4 years ago
  4. Ajax ordering models on the change list page of the admin using drag and drop with jQuery UI by spoof 2 years, 11 months ago
  5. Drag and drop ordering of admin list elements for Grappelli by sjaensch 2 years, 4 months ago

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()
});

#

vedran (on July 16, 2009):

hi! my item model has two ForeignKey fields and ordering is working but when i save the form it rearanges as in initial situation, as no ordering was made after making a few entries. does someone has a hint?

here is my 'Item' class from model.py

class PhotoSetPhotoItem(models.Model): #mid = models.AutoField(primary_key=True) order = models.IntegerField(blank = True, null = True) photo_set = models.ForeignKey('PhotoSet', related_name='photoset') photo_item = models.ForeignKey('Photo', related_name='photoitem') class Meta: #db_table='photoset_photoitems' ordering = ['order']

def __unicode__(self):
    return str(self.order)+str(self.mid)

#

vedran (on July 16, 2009):

cant make markdown working...

#

dfolland (on October 13, 2009):

for vedran: "name" and "order" are 2 required fields in the original code. Use leplatrem's code (June 26) which only requires "order"

#

balexand (on December 30, 2009):

The script above doesn't set the order fields properly if the fields are moved before the name has been entered. It's easier to just update the order fields before the form submits like this:

/* menu-sort.js */

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

#

ugurozyilmazel (on February 26, 2010):

thanx a lot!

#

eddified (on April 9, 2010):

I used leplatrem's June 26 comment but I had to change the first line to this:

jQuery(function($) {

because something else in the page called jquery.noConflict() and it was messing up use of the dollar sign.

I also had to change vTextField to vIntegerField -- not sure why I had to do this, maybe it has to do with my django version, which is 1.2.0 beta 1

And then I also changed this:

$(this).find('input[id$=parent_order]').parent('div').parent('div').hide();

to this:

$(this).find('input[id$=parent_order]').parent('div').parent('div').hide().parent().parent().css('cursor','move');

Which helps the user know that the items are orderable (otherwise there is no indication in the U.I. that the items are orderable).

Here is my final code:

jQuery(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('.vIntegerField').val()) {
                    $(this).find('input[id$=parent_order]').val(i+1);
                }
        });
        }
    });
    $("div.inline-group").disableSelection();

});

jQuery(document).ready(function($){
    $(this).find('input[id$=parent_order]').parent('div').parent('div').hide().parent().parent().css('cursor','move');
});

#

eddified (on April 9, 2010):

replace ALL occurrences of "parent_order" with just "order" in my previous post.

#

metamemetics (on May 19, 2010):

A quick hack of eddified's post to work with Django 1.2 and update the order field with dynamic inline creation:

jQuery(function($) {
    $("div.inline-group").sortable({ 
        axis: 'y',
        placeholder: 'ui-state-highlight', 
        forcePlaceholderSize: 'true', 
        items: '.row1, .row2', 
        update: update
    });
    $("div.inline-group").disableSelection();
});
function update() {
    $('.row1, .row2').each(function(i) {
        $(this).find('input[id$=order]').val(i+1);
    });
}
jQuery(document).ready(function($){
    $(this).find('input[id$=order]').parent('div').parent('div').hide().parent().parent().css('cursor','move');
    $('.add-row a').click(update);
});

#

proppy (on May 27, 2010):

Hi,

I wrote some test cases for this snippet when importing it to playground project:

Hope that helps.

#

proppy (on June 3, 2010):

Since $('.add-row a') button is added dynamicaly from django admin javascript (inlines.js), dependending of the admin javascript load order you might have to override admin template using http://stackoverflow.com/questions/1164358/django-admin-custom-javascript-load-order

#

payala (on August 24, 2010):

I had to modify the following in order to hide the order field in Django 1.2.1.

I changed the line:

$(this).find('input[id$=order]').parent('div').parent('div').hide().parent().parent().css('cursor','move');

to:

$(this).find('input[id$=order]').parent('td').hide().parent('tr').parent('tbody').parent('table').find("th:contains('Order')").hide(); $(this).find('input[id$=order]').parent('td').hide().parent('tr').parent('tbody').parent('table').css('cursor','move');

#

centralniak (on September 30, 2010):

Basing on your snippet, I made a simple Django app that makes the implementation even faster: http://pypi.python.org/pypi/django-inline-ordering

Your suggestions are welcome!

#

IvaCOMPTON (on June 15, 2011):

I had got a desire to begin my own company, but I didn't have got enough of cash to do that. Thank God my close fellow advised to use the credit loans. So I used the credit loan and realized my old dream.

#

RobertPattinson (on August 6, 2012):

Hi,

I wrote some test cases for this snippet when importing it to playground project:
Thanks
Cases for blackberry curve 9320

#

siblek31 (on April 13, 2013):

Perancis (English Listeni / fræns / FRANSS atau / frɑ ː ns / FRAHNSS, Perancis: [fʁɑs] (mendengarkan)), secara resmi Republik Perancis (French: République française pengucapan Perancis: [ʁepyblik fʁɑsɛz]), adalah sebuah republik semi-presidensial uniter kebanyakan berlokasi di Eropa Barat, [catatan 12] dengan beberapa daerah di luar negeri dan wilayah. Perancis Metropolitan memanjang dari Laut Mediterania hingga Selat Inggris dan Laut Utara, dan dari Rhine ke Samudera Atlantik. Dari bentuknya, sering disebut dalam bahasa Prancis sebagai l'Hexagone ("The Hexagon").

Perancis cara cepat hamil adalah negara terbesar di Eropa Barat dan ketiga terbesar di Eropa secara keseluruhan. Ia memiliki zona ekonomi eksklusif soal ulangan sd kedua terbesar di dunia. Perancis telah menjadi kekuatan utama dengan pengaruh budaya, ekonomi, militer, dan politik kursus teknisi komputer yang kuat di Eropa dan di seluruh dunia [6] Perancis memiliki cita-cita utamanya dinyatakan dalam Deklarasi abad ke-18 dari Hak Asasi Manusia dan Warga Negara.. Dari ke-17 sampai awal abad ke-20, Prancis membangun imperium kolonial kursus bahasa inggris murah terbesar kedua waktu itu, penguasa sebagian besar pertama Amerika Utara dan India dan kemudian Northwest dan Afrika Tengah; Madagaskar, Indochina cara mendapatkan uang dari internet dan China tenggara, dan banyak Karibia dan Pasifik Islands .

#

(Forgotten your password?)