Django

Code

Ticket #13: inline-tabular.diff

File inline-tabular.diff, 11.8 kB (added by barbuza, 2 years ago)

dynamic adding and ajax deleting for tabular mode. to enable, add js to Media class in ModelAdmin? of your models

  • django/contrib/admin/media/css/global.css

    old new  
    139139/* OBJECT HISTORY */ 
    140140table#change-history { width:100%; } 
    141141table#change-history tbody th { width:16em; } 
     142 
     143/* INLINE DELETING */ 
     144td.delete { width: 20px; } 
     145#ajax-indicator { position: absolute; top: 0px; right: 0px; padding: 5px 10px; border: 1px solid #ddd; background: #ffc; } 
  • django/contrib/admin/media/js/admin/InlineObjectsTabular.js

    old new  
     1(function() { 
     2 
     3    if (! gettext) gettext = function(x) { return x} 
     4 
     5    String.prototype.trim = function() { 
     6        return this.replace(/^\s+|\s+$/g, ''); 
     7    } 
     8 
     9    Array.prototype.filter = function(fn) { 
     10        var results = []; 
     11                for (var i = 0, l = this.length; i < l; i++){ 
     12                        if (fn(this[i])) results.push(this[i]); 
     13                } 
     14                return results; 
     15    } 
     16 
     17    Array.prototype.map = function(fn) { 
     18        var results = []; 
     19                for (var i = 0, l = this.length; i < l; i++){ 
     20                        results.push(fn(this[i])); 
     21                } 
     22                return results; 
     23    } 
     24 
     25        Array.prototype.flatten = function(){ 
     26                var array = []; 
     27                for (var i = 0, l = this.length; i < l; i++){ 
     28                        array = array.concat(this[i]); 
     29                } 
     30                return array; 
     31        } 
     32 
     33    function $A(e) { 
     34        var r = []; 
     35        for(var i=0, l=e.length; i<l; i++) r.push(e[i]); 
     36        return r; 
     37    } 
     38 
     39    function _prepare_selector(selector, point) { 
     40        var queue = selector.split(/\s/).map(function(x){return x.trim()}); 
     41        var points = [point]; 
     42        for(var i=0, l=queue.length; i<l; i++) { 
     43            var new_points = []; 
     44            for(var j=0, k=points.length; j<k; j++) { 
     45                var tmp = _select(queue[i], points[j]); 
     46                if (!tmp) break; 
     47                for(var p=0, o=tmp.length; p<o; p++) { 
     48                    new_points.push(tmp[p]); 
     49                } 
     50            } 
     51            points = new_points; 
     52        } 
     53        return points; 
     54    } 
     55     
     56    function _select(selector, point) { 
     57        var chunks = selector.split(/[\.]/); 
     58        var splitters = selector.match(/[\.]/g); 
     59        if (splitters) { 
     60            if(chunks.length > splitters.length){ 
     61                splitters.unshift(null); 
     62            } 
     63            var f_ch = chunks.shift(), f_sel = splitters.shift(); 
     64            if (f_sel == '.') { 
     65                els=point.getElementsByClassName(f_ch); 
     66            } else if (f_sel == null) { 
     67                els = point.getElementsByTagName(f_ch); 
     68            } 
     69            els = $A(els); 
     70            for(var i=0, l=chunks.length; i<l; i++){ 
     71                var ch = chunks[i], sel = splitters[i], tmp = []; 
     72                 
     73                for(var j=0, k=els.length; j<k; j++){ 
     74                    if(els[j].className.match(ch)) tmp.push(els[j]); 
     75                } 
     76                els = tmp; 
     77            } 
     78            return $A(els); 
     79             
     80        } else { 
     81            return $A(point.getElementsByTagName(chunks.shift())); 
     82        } 
     83    } 
     84     
     85    function show_loading() { 
     86        hide_loading(); 
     87        var l = document.createElement('div'); 
     88        l.appendChild(document.createTextNode(gettext('loading ...'))); 
     89        l.id = 'ajax-indicator'; 
     90        document.body.appendChild(l); 
     91    } 
     92     
     93    function hide_loading() { 
     94        var l = $('ajax-indicator'); 
     95        if(l) l.parentNode.removeChild(l); 
     96    } 
     97     
     98    function $(e) { 
     99        return document.getElementById(e); 
     100    } 
     101     
     102    function $$(e, point) { 
     103        point = point ? point : document; 
     104        return e.split(',') 
     105            .map(function(x){return x.trim()}) 
     106            .filter(function(x){return !!x}) 
     107            .map(function(x){return _prepare_selector(x, point)}) 
     108            .flatten(); 
     109    } 
     110 
     111    function make_delete_handle(h) { 
     112        var p = h.parentNode; 
     113        p.removeChild(h); 
     114        var origin = $$('td.original input', p.parentNode)[0]; 
     115        if (! origin) return; 
     116        var id = origin.value, name = origin.name.match(/^(\w+)-/i)[1]; 
     117        var d_handle = document.createElement('span'); 
     118        d_handle.className = 'inline-deletelink'; 
     119        addEvent(d_handle, 'click', function(){ 
     120            show_loading(); 
     121            xmlhttp.open('POST', 'delete-inline/'+name+'/'+id+'/', true); 
     122            function onStateChange() { 
     123                if (xmlhttp.readyState != 4) return; 
     124                hide_loading(); 
     125                if ((xmlhttp.status >= 200) && (xmlhttp.status < 300)){ 
     126                    var response; 
     127                    eval('response = '+xmlhttp.responseText); 
     128                    if(response.error) { 
     129                        alert(response.error); 
     130                        return; 
     131                    } 
     132                    var tc = $('id_'+name+'-TOTAL_FORMS'); 
     133                    tc.value = Math.ceil(tc.value) - 1; 
     134                    var ic = $('id_'+name+'-INITIAL_FORMS'); 
     135                    ic.value = Math.ceil(ic.value) - 1; 
     136                    var tbody = p.parentNode.parentNode; 
     137                    p.parentNode.parentNode.removeChild(p.parentNode); 
     138                    var trs = $$('tr', tbody); 
     139                    for(var i=0, l=trs.length; i<l; i++){ 
     140                        trs[i].className = trs[i].className.replace(/row\d/, i % 2 ? 'row2' : 'row1'); 
     141                        function fix_id_and_name(input) { 
     142                            input.name = input.name.replace(/-\d-/, '-'+i+'-'); 
     143                            input.id = input.id.replace(/-\d-/, '-'+i+'-'); 
     144                        } 
     145                        $$('input', trs[i]).map(fix_id_and_name); 
     146                    } 
     147                     
     148                } else { 
     149                    // strange 
     150                } 
     151            } 
     152            xmlhttp.onreadystatechange = onStateChange; 
     153            xmlhttp.send(null); 
     154        }) 
     155        p.appendChild(d_handle); 
     156    } 
     157 
     158    function make_add_link(group){ 
     159         
     160        var rows = $$('tr', group); 
     161        var origin = $$('td.original input', group)[0]; 
     162        if(! origin) return; 
     163        var name = origin.name.match(/^(\w+)-/i)[1]; 
     164        var tc = $('id_'+name+'-TOTAL_FORMS'); 
     165        last_row = rows[rows.length-1]; 
     166        var tmp_row = document.createElement('tr'); 
     167        var tds = $$('td', last_row); 
     168        for(var i=0, l=tds.length; i<l; i++){ 
     169            var td = tmp_row.appendChild(document.createElement('td')); 
     170            td.className = tds[i].className; 
     171            var inputs = $$('input, textarea', tds[i]); 
     172            for(var j=0, k=inputs.length; j<k; j++){ 
     173                var o_input = inputs[j];             
     174                var input = td.appendChild(document.createElement(o_input.nodeName)); 
     175                input.type = o_input.type; 
     176                input.id = o_input.id.replace(/-\d-/, '---'); 
     177                input.name = o_input.name.replace(/-\d-/, '---'); 
     178            } 
     179        } 
     180         
     181        function add_new() { 
     182            var tbody = $$('tbody', group)[0]; 
     183            var tr = document.createElement('tr'); 
     184            var tds = $$('td', tmp_row); 
     185            tc.value = Math.ceil(tc.value) + 1; 
     186            var id = tc.value - 1; 
     187            for(var i=0, l=tds.length; i<l; i++){ 
     188                var td = tr.appendChild(document.createElement('td')); 
     189                td.className = tds[i].className; 
     190                var inputs = $$('input, textarea', tds[i]); 
     191                for(var j=0, k=inputs.length; j<k; j++){ 
     192                    var o_input = inputs[j]; 
     193                    var input = td.appendChild(document.createElement(o_input.nodeName)); 
     194                    input.type = o_input.type; 
     195                    input.id = o_input.id.replace(/---/, '-'+id+'-'); 
     196                    input.name = o_input.name.replace(/---/, '-'+id+'-'); 
     197                } 
     198            } 
     199            tr.className = id % 2 ? 'row2' : 'row1'; 
     200            tbody.appendChild(tr); 
     201        } 
     202         
     203        var link = document.createElement('a'); 
     204        link.className = 'addlink'; 
     205        link.href = 'javascript:(function(){})()'; 
     206        link.appendChild(document.createTextNode(gettext('Add another'))); 
     207        addEvent(link, 'click', add_new); 
     208        group.appendChild(link); 
     209         
     210        tc.value = $$('tr.row1, tr.row2', group).length; 
     211    } 
     212 
     213    function init_tabular_deleting() { 
     214        var trs = $$('div.inline-related.tabular thead tr'); 
     215        for(var i=0, l=trs.length; i<l; i++) { 
     216            var ths = $$('th', trs[i]); 
     217            ths[ths.length-1].firstChild.data = ''; 
     218            ths[ths.length-1].style.borderLeft = 'none'; 
     219        } 
     220        $$('div.inline-related.tabular').map(make_add_link); 
     221        $$('div.inline-related.tabular td.delete input').map(make_delete_handle); 
     222    } 
     223 
     224    addEvent(window, 'load', init_tabular_deleting); 
     225 
     226})(); 
  • django/contrib/admin/options.py

    old new  
    248248            return self.history_view(request, unquote(url[:-8])) 
    249249        elif url.endswith('delete'): 
    250250            return self.delete_view(request, unquote(url[:-7])) 
     251        elif url.count('delete-inline'): 
     252            return self.delete_inline_view(request, unquote(url)) 
    251253        else: 
    252254            return self.change_view(request, unquote(url)) 
    253255 
     
    667669            "admin/delete_confirmation.html" 
    668670        ], context, context_instance=template.RequestContext(request)) 
    669671 
     672    def delete_inline_view(self, request, rest, extra_context=None): 
     673        """ 'delete-inline' view for this model """ 
     674         
     675        from django.http import HttpResponse 
     676        from django.utils import simplejson 
     677        from django.core.exceptions import ObjectDoesNotExist 
     678         
     679        def json(data): 
     680            return HttpResponse(simplejson.dumps(data), mimetype='text/x-json') 
     681         
     682        try: 
     683            parent_id, trash, set_name, child_id = filter(None, rest.split('/')) 
     684        except ValueError: 
     685            return json({'error': _('Invalid request.')}) 
     686         
     687        if request.method != 'POST': 
     688            return json({'error': _('Request method should be post.')}) 
     689         
     690        from django.contrib.admin.models import LogEntry, DELETION 
     691        opts = self.model._meta 
     692        app_label = opts.app_label 
     693 
     694        try: 
     695            parent = self.model._default_manager.get(pk=parent_id) 
     696        except self.model.DoesNotExist: 
     697            parent = None 
     698 
     699        try: 
     700            _set = getattr(parent, set_name) 
     701        except AttributeError: 
     702            return json({'error': _('Unknown "%s" for "%s".') % (set_name, self.model)}) 
     703 
     704        try: 
     705            obj = _set.get(pk=child_id) 
     706        except ObjectDoesNotExist: 
     707            obj = None 
     708             
     709        if obj is None: 
     710            return json({'error': _('Object does not exists.')}) 
     711                 
     712        perms_needed = sets.Set() 
     713        get_deleted_objects([], perms_needed, request.user, obj, obj._meta, 1, self.admin_site) 
     714         
     715        if perms_needed: 
     716            return json({'error': _('Not enought permissions to delete related objects.')}) 
     717         
     718        obj.delete() 
     719        LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(obj.__class__).id, obj, str(obj), DELETION) 
     720 
     721        return json({'error': None}) 
     722 
    670723    def history_view(self, request, object_id, extra_context=None): 
    671724        "The 'history' admin view for this model." 
    672725        from django.contrib.admin.models import LogEntry