My favorites
▼
|
Sign in
django-mptt
Utilities for implementing Modified Preorder Tree Traversal
Project Home
Downloads
Wiki
Issues
Source
Export to GitHub
READ-ONLY: This project has been
archived
. For more information see
this post
.
Search
Search within:
All issues
Open issues
New issues
Issues to verify
for
Advanced search
Search tips
Subscriptions
Issue
33
attachment: mptt_admin.diff
(7.6 KB)
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
Index: mptt/admin.py
===================================================================
--- mptt/admin.py (revision 0)
+++ mptt/admin.py (revision 0)
@@ -0,0 +1,147 @@
+from django import template, forms
+from django.contrib import admin
+from django.contrib.admin.templatetags.admin_list import items_for_result, result_headers
+from django.contrib.admin.util import quote
+from django.contrib.admin.views.main import IS_POPUP_VAR
+from django.db import models, connection, transaction
+from django.http import HttpResponse, HttpResponseBadRequest
+from django.shortcuts import render_to_response
+from django.utils import simplejson
+from django.utils.encoding import force_unicode
+from django.utils.translation import ugettext
+
+qn = connection.ops.quote_name
+
+# hacky!
+class ChangeList(object):
+ def __init__(self, request, model, list_display, list_display_links, list_select_related, list_per_page, model_admin):
+ self.model = model
+ self.opts = model._meta
+ self.lookup_opts = self.opts
+ self.list_display = list_display
+ self.list_display_links = list_display_links
+ self.list_select_related = list_select_related
+ self.list_per_page = list_per_page
+ self.model_admin = model_admin
+ self.pk_attname = self.lookup_opts.pk.attname
+
+ self.to_field = False
+ self.is_popup = False
+ self.order_field = ''
+ self.order_type = ''
+
+ def get_query_string(self, new_params=None, remove=None):
+ pass
+
+ def url_for_result(self, result):
+ return "%s/" % quote(getattr(result, self.pk_attname))
+
+def _build_tree(nodes, cl):
+ ret = []
+ for obj in nodes:
+ dic = {
+ 'id': obj.id,
+ 'info': list(items_for_result(cl, obj)),
+ }
+ if not obj.is_leaf_node():
+ dic['children'] = _build_tree(obj.get_children(), cl)
+ ret.append(dic)
+ return ret
+
+# This is lifted straight from tusk, it could probably do with rethinking, or at
+# least cleaning up
+def _left_right(structure, counter=1, parent=None, level=0):
+ # id tree_id parent left right
+ ret = []
+ for elem in structure:
+ item = [_left_right.tree, parent, counter, 0, level, int(elem['id'])]
+ children = elem.get('children')
+ counter += 1
+ if children:
+ arr, counter = _left_right(children, counter, int(elem['id']), level+1)
+ ret += arr
+ item[3] = counter
+ counter += 1
+ ret.append(item)
+ if parent == 'NULL':
+ _left_right.tree += 1
+ counter = 1
+ return ret, counter
+
+class MpttModelAdmin(admin.ModelAdmin):
+ """An admin that will display a drag and droppable list for moving nodes."""
+ def __call__(self, request, url):
+ if url == "json":
+ return self.json_view(request)
+ return super(MpttModelAdmin, self).__call__(request, url)
+
+ def json_view(self, request):
+ if request.method == "POST":
+ try:
+ tree = simplejson.loads(request.POST["nested-sortable-widget"])
+ except (KeyError, ValueError):
+ return HttpResponseBadRequest()
+ _left_right.tree = 1
+ updates, counter = _left_right(tree["items"])
+ cursor = connection.cursor()
+ opts = self.model._meta
+ cursor.executemany("""
+ UPDATE %(table)s
+ SET %(tree_id)s = %%s,
+ %(parent)s = %%s,
+ %(left)s = %%s,
+ %(right)s = %%s,
+ %(level)s = %%s
+ WHERE %(pk)s = %%s""" % {
+ "table": qn(opts.db_table),
+ 'level': qn(opts.get_field(opts.level_attr).column),
+ 'left': qn(opts.get_field(opts.left_attr).column),
+ 'tree_id': qn(opts.get_field(opts.tree_id_attr).column),
+ 'right': qn(opts.get_field(opts.right_attr).column),
+ 'parent': qn(opts.get_field(opts.parent_attr).column),
+ 'pk': qn(opts.pk.column),
+ }, updates)
+ transaction.commit_unless_managed()
+ return HttpResponse()
+ cl = ChangeList(request, self.model, self.list_display,
+ self.list_display_links, self.list_select_related,
+ self.list_per_page, self)
+ # Force load this so the count doesn't hit the database
+ items = list(_build_tree(self.model.tree.root_nodes(), cl))
+ count = self.model._default_manager.count()
+ return HttpResponse(simplejson.dumps({
+ 'requestFirstIndex': 0,
+ 'firstIndex': 0,
+ # capitalise first character of each heading
+ 'columns': [d["text"] and d["text"][0].upper() + d["text"][1:]
+ for d in result_headers(cl)],
+ 'count': count,
+ 'totalCount': count,
+ 'items': items,
+ }), mimetype='text/plain')
+
+ def changelist_view(self, request, extra_context=None):
+ if IS_POPUP_VAR in request.GET:
+ return super(MpttModelAdmin, self).changelist_view(
+ request, extra_context)
+ opts = self.model._meta
+ context = {
+ 'root_path': self.admin_site.root_path,
+ 'opts': opts,
+ 'app_label': opts.app_label,
+ 'has_add_permission': self.has_add_permission(request),
+ 'list_per_page': self.list_per_page,
+ 'title': (ugettext('Select %s to change') %
+ force_unicode(opts.verbose_name)),
+ 'media': forms.Media(
+ js=["mptt/jquery-1.2.6.min.js",
+ "mptt/json.js",
+ "mptt/interface-1.2.js",
+ "mptt/inestedsortable.js",
+ "mptt/jquery.nestedsortablewidget.js",],
+ css={'all': ["mptt/nestedsortablewidget.css",]}),
+ }
+ context.update(extra_context or {})
+ return render_to_response("admin/mptt/change_list.html", context,
+ context_instance=template.RequestContext(request))
+
\ No newline at end of file
Index: mptt/templates/admin/mptt/change_list.html
===================================================================
--- mptt/templates/admin/mptt/change_list.html (revision 0)
+++ mptt/templates/admin/mptt/change_list.html (revision 0)
@@ -0,0 +1,50 @@
+{% extends "admin/change_list.html" %}
+{% load adminmedia admin_list i18n %}
+
+{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans "Home" %}</a> › <a href="../">{{ app_label|capfirst }}</a> › {{ opts.verbose_name_plural|capfirst }}</div>{% endblock %}
+
+{% block extrahead %}{{ block.super }}
+{{ media }}
+<script type="text/javascript">
+//<![CDATA[
+
+function buildNSW()
+{
+ $('#my_widget').NestedSortableWidget({
+ loadUrl: "json/?rnd="+Math.random(),
+ serializeWithJSON: true,
+ nestedSortCfg: {
+ nestingPxSpace: '15'
+ },
+ onSave: function(){
+ $('#my_widget').NestedSortableWidgetDestroy();
+ buildNSW();
+ },
+ });
+}
+
+$(window).load(function(){
+ buildNSW();
+});
+
+//]]>
+</script>
+<style type="text/css" media="screen">
+ ul li {
+ list-style-type: none;
+ }</style>
+{% endblock %}
+
+{% block content %}
+<div id="content-main">
+ {% block object-tools %}
+ {% if has_add_permission %}
+ <ul class="object-tools"><li><a href="add/{% if is_popup %}?_popup=1{% endif %}" class="addlink">{% blocktrans with opts.verbose_name as name %}Add {{ name }}{% endblocktrans %}</a></li></ul>
+ {% endif %}
+ {% endblock %}
+
+<div id="my_widget"></div>
+
+</div>
+
+{% endblock %}
\ No newline at end of file
Powered by
Google Project Hosting