django-fast-treenode 1.1.0__tar.gz → 1.1.2__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {django-fast-treenode-1.1.0/django_fast_treenode.egg-info → django-fast-treenode-1.1.2}/PKG-INFO +6 -7
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/README.md +4 -3
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2/django_fast_treenode.egg-info}/PKG-INFO +6 -7
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/setup.cfg +1 -1
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/treenode/admin.py +136 -136
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/treenode/compat.py +8 -8
- {django-fast-treenode-1.1.0/treenode/static/treenode/css → django-fast-treenode-1.1.2/treenode/docs}/.gitkeep +1 -1
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/treenode/docs/Documentation +28 -9
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/treenode/forms.py +32 -32
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/treenode/models.py +15 -14
- {django-fast-treenode-1.1.0/treenode/docs → django-fast-treenode-1.1.2/treenode/static/select2tree}/.gitkeep +1 -1
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/treenode/static/treenode/.gitkeep +1 -1
- {django-fast-treenode-1.1.0/treenode/static/select2tree → django-fast-treenode-1.1.2/treenode/static/treenode/css}/.gitkeep +1 -1
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/treenode/static/treenode/css/treenode.css +84 -84
- django-fast-treenode-1.1.2/treenode/static/treenode/js/.gitkeep +1 -0
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/treenode/static/treenode/js/treenode.js +201 -201
- django-fast-treenode-1.1.2/treenode/templates/.gitkeep +1 -0
- django-fast-treenode-1.1.2/treenode/templates/widgets/.gitkeep +1 -0
- django-fast-treenode-1.1.0/treenode/static/treenode/js/.gitkeep +0 -1
- django-fast-treenode-1.1.0/treenode/templates/.gitkeep +0 -1
- django-fast-treenode-1.1.0/treenode/templates/widgets/.gitkeep +0 -1
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/LICENSE +0 -0
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/MANIFEST.in +0 -0
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/django_fast_treenode.egg-info/SOURCES.txt +0 -0
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/django_fast_treenode.egg-info/dependency_links.txt +0 -0
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/django_fast_treenode.egg-info/requires.txt +0 -0
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/django_fast_treenode.egg-info/top_level.txt +0 -0
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/pyproject.toml +0 -0
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/setup.py +0 -0
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/treenode/__init__.py +0 -0
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/treenode/apps.py +0 -0
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/treenode/factory.py +0 -0
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/treenode/managers.py +0 -0
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/treenode/static/.gitkeep +0 -0
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/treenode/static/select2tree/select2tree.css +0 -0
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/treenode/static/select2tree/select2tree.js +0 -0
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/treenode/templates/widgets/attrs.html +0 -0
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/treenode/templates/widgets/options.html +0 -0
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/treenode/templates/widgets/select2tree.html +0 -0
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/treenode/tests.py +0 -0
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/treenode/version.py +0 -0
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/treenode/views.py +0 -0
- {django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2}/treenode/widgets.py +0 -0
{django-fast-treenode-1.1.0/django_fast_treenode.egg-info → django-fast-treenode-1.1.2}/PKG-INFO
RENAMED
@@ -1,12 +1,11 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: django-fast-treenode
|
3
|
-
Version: 1.1.
|
3
|
+
Version: 1.1.2
|
4
4
|
Summary: Application for supporting tree (hierarchical) data structure in Django projects
|
5
5
|
Home-page: https://github.com/TimurKady/fast-treenode
|
6
6
|
Author: Timur Kady
|
7
7
|
Author-email: timurkady@yandex.com
|
8
8
|
License: MIT
|
9
|
-
Platform: UNKNOWN
|
10
9
|
Classifier: Environment :: Web Environment
|
11
10
|
Classifier: Framework :: Django
|
12
11
|
Classifier: Framework :: Django :: 3.0
|
@@ -21,13 +20,15 @@ Classifier: Programming Language :: Python :: 3.9
|
|
21
20
|
Requires-Python: >=3.8
|
22
21
|
Description-Content-Type: text/markdown
|
23
22
|
License-File: LICENSE
|
23
|
+
Requires-Dist: Django>=3.0
|
24
24
|
|
25
25
|
# Django-fast-treenode
|
26
26
|
__Combination of Adjacency List and Closure Table__
|
27
27
|
|
28
28
|
## Functions
|
29
29
|
Application for supporting tree (hierarchical) data structure in Django projects
|
30
|
-
*
|
30
|
+
* fast: the fastest of the two methods is used to process requests, combining the advantages of an **Adjacency Table** and a **Closure Table**,
|
31
|
+
* even faster: the main resource-intensive operations are **cached**; **bulk operations** are used for inserts and changes,
|
31
32
|
* synchronized: model instances in memory are automatically updated,
|
32
33
|
* compatibility: you can easily add a tree node to existing projects using TreeNode without changing the code,
|
33
34
|
* no dependencies,
|
@@ -572,9 +573,9 @@ cls.update_tree()
|
|
572
573
|
Released under [MIT License](https://github.com/TimurKady/django-fast-treenode/blob/main/LICENSE).
|
573
574
|
|
574
575
|
## Cautions
|
575
|
-
The code
|
576
|
+
The provided code is already being used in production projects, even though I have only done general testing. That is why the risk of using the code lies entirely with you.
|
576
577
|
|
577
|
-
|
578
|
+
**Warning**: don't access the tree node fields directly! Most of them have been removed as unnecessary. In the future, only `tn_parent` and `tn_priority` will be kept. Use the functions described in the documentation above or the documentation for the [original application](https://github.com/fabiocaccamo/django-treenode).
|
578
579
|
|
579
580
|
## Credits
|
580
581
|
This software contains, uses, including in a modified form:
|
@@ -591,5 +592,3 @@ Future plans:
|
|
591
592
|
|
592
593
|
|
593
594
|
Your wishes, objections, comments are welcome.
|
594
|
-
|
595
|
-
|
@@ -3,7 +3,8 @@ __Combination of Adjacency List and Closure Table__
|
|
3
3
|
|
4
4
|
## Functions
|
5
5
|
Application for supporting tree (hierarchical) data structure in Django projects
|
6
|
-
*
|
6
|
+
* fast: the fastest of the two methods is used to process requests, combining the advantages of an **Adjacency Table** and a **Closure Table**,
|
7
|
+
* even faster: the main resource-intensive operations are **cached**; **bulk operations** are used for inserts and changes,
|
7
8
|
* synchronized: model instances in memory are automatically updated,
|
8
9
|
* compatibility: you can easily add a tree node to existing projects using TreeNode without changing the code,
|
9
10
|
* no dependencies,
|
@@ -548,9 +549,9 @@ cls.update_tree()
|
|
548
549
|
Released under [MIT License](https://github.com/TimurKady/django-fast-treenode/blob/main/LICENSE).
|
549
550
|
|
550
551
|
## Cautions
|
551
|
-
The code
|
552
|
+
The provided code is already being used in production projects, even though I have only done general testing. That is why the risk of using the code lies entirely with you.
|
552
553
|
|
553
|
-
|
554
|
+
**Warning**: don't access the tree node fields directly! Most of them have been removed as unnecessary. In the future, only `tn_parent` and `tn_priority` will be kept. Use the functions described in the documentation above or the documentation for the [original application](https://github.com/fabiocaccamo/django-treenode).
|
554
555
|
|
555
556
|
## Credits
|
556
557
|
This software contains, uses, including in a modified form:
|
{django-fast-treenode-1.1.0 → django-fast-treenode-1.1.2/django_fast_treenode.egg-info}/PKG-INFO
RENAMED
@@ -1,12 +1,11 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: django-fast-treenode
|
3
|
-
Version: 1.1.
|
3
|
+
Version: 1.1.2
|
4
4
|
Summary: Application for supporting tree (hierarchical) data structure in Django projects
|
5
5
|
Home-page: https://github.com/TimurKady/fast-treenode
|
6
6
|
Author: Timur Kady
|
7
7
|
Author-email: timurkady@yandex.com
|
8
8
|
License: MIT
|
9
|
-
Platform: UNKNOWN
|
10
9
|
Classifier: Environment :: Web Environment
|
11
10
|
Classifier: Framework :: Django
|
12
11
|
Classifier: Framework :: Django :: 3.0
|
@@ -21,13 +20,15 @@ Classifier: Programming Language :: Python :: 3.9
|
|
21
20
|
Requires-Python: >=3.8
|
22
21
|
Description-Content-Type: text/markdown
|
23
22
|
License-File: LICENSE
|
23
|
+
Requires-Dist: Django>=3.0
|
24
24
|
|
25
25
|
# Django-fast-treenode
|
26
26
|
__Combination of Adjacency List and Closure Table__
|
27
27
|
|
28
28
|
## Functions
|
29
29
|
Application for supporting tree (hierarchical) data structure in Django projects
|
30
|
-
*
|
30
|
+
* fast: the fastest of the two methods is used to process requests, combining the advantages of an **Adjacency Table** and a **Closure Table**,
|
31
|
+
* even faster: the main resource-intensive operations are **cached**; **bulk operations** are used for inserts and changes,
|
31
32
|
* synchronized: model instances in memory are automatically updated,
|
32
33
|
* compatibility: you can easily add a tree node to existing projects using TreeNode without changing the code,
|
33
34
|
* no dependencies,
|
@@ -572,9 +573,9 @@ cls.update_tree()
|
|
572
573
|
Released under [MIT License](https://github.com/TimurKady/django-fast-treenode/blob/main/LICENSE).
|
573
574
|
|
574
575
|
## Cautions
|
575
|
-
The code
|
576
|
+
The provided code is already being used in production projects, even though I have only done general testing. That is why the risk of using the code lies entirely with you.
|
576
577
|
|
577
|
-
|
578
|
+
**Warning**: don't access the tree node fields directly! Most of them have been removed as unnecessary. In the future, only `tn_parent` and `tn_priority` will be kept. Use the functions described in the documentation above or the documentation for the [original application](https://github.com/fabiocaccamo/django-treenode).
|
578
579
|
|
579
580
|
## Credits
|
580
581
|
This software contains, uses, including in a modified form:
|
@@ -591,5 +592,3 @@ Future plans:
|
|
591
592
|
|
592
593
|
|
593
594
|
Your wishes, objections, comments are welcome.
|
594
|
-
|
595
|
-
|
@@ -4,7 +4,7 @@ tag_date = 0
|
|
4
4
|
|
5
5
|
[metadata]
|
6
6
|
name = django-fast-treenode
|
7
|
-
version = 1.1.
|
7
|
+
version = 1.1.2
|
8
8
|
description = Application for supporting tree (hierarchical) data structure in Django projects
|
9
9
|
long_description_content_type = text/markdown
|
10
10
|
long_description = file: README.md
|
@@ -1,136 +1,136 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
"""
|
3
|
-
TreeNode Admin Module
|
4
|
-
|
5
|
-
"""
|
6
|
-
|
7
|
-
from django.contrib import admin
|
8
|
-
from django.utils.safestring import mark_safe
|
9
|
-
from django.contrib.admin.views.main import ChangeList
|
10
|
-
from .forms import TreeNodeForm
|
11
|
-
|
12
|
-
|
13
|
-
class NoPkDescOrderedChangeList(ChangeList):
|
14
|
-
def get_ordering(self, request, queryset):
|
15
|
-
rv = super().get_ordering(request, queryset)
|
16
|
-
rv = list(rv)
|
17
|
-
rv.remove('-pk') if '-pk' in rv else None
|
18
|
-
return tuple()
|
19
|
-
|
20
|
-
def get_queryset(self, request):
|
21
|
-
qs = self.model.objects.all()
|
22
|
-
return qs.select_related('tn_parent')
|
23
|
-
|
24
|
-
|
25
|
-
class TreeNodeModelAdmin(admin.ModelAdmin):
|
26
|
-
|
27
|
-
TREENODE_DISPLAY_MODE_ACCORDION = 'accordion'
|
28
|
-
TREENODE_DISPLAY_MODE_BREADCRUMBS = 'breadcrumbs'
|
29
|
-
TREENODE_DISPLAY_MODE_INDENTATION = 'indentation'
|
30
|
-
|
31
|
-
treenode_display_mode = TREENODE_DISPLAY_MODE_INDENTATION
|
32
|
-
|
33
|
-
form = TreeNodeForm
|
34
|
-
list_per_page = 1000
|
35
|
-
|
36
|
-
def get_list_display(self, request):
|
37
|
-
base_list_display = super(
|
38
|
-
TreeNodeModelAdmin, self).get_list_display(request)
|
39
|
-
base_list_display = list(base_list_display)
|
40
|
-
|
41
|
-
def treenode_field_display(obj):
|
42
|
-
return self._get_treenode_field_display(request, obj)
|
43
|
-
|
44
|
-
treenode_field_display.short_description = self.model._meta.verbose_name
|
45
|
-
treenode_field_display.allow_tags = True
|
46
|
-
|
47
|
-
if len(base_list_display) == 1 and base_list_display[0] == '__str__':
|
48
|
-
return (treenode_field_display, )
|
49
|
-
else:
|
50
|
-
treenode_display_field = getattr(
|
51
|
-
self.model, 'treenode_display_field')
|
52
|
-
if len(base_list_display) >= 1 and base_list_display[0] == treenode_display_field:
|
53
|
-
base_list_display.pop(0)
|
54
|
-
return (treenode_field_display, ) + tuple(base_list_display)
|
55
|
-
|
56
|
-
return base_list_display
|
57
|
-
|
58
|
-
def get_changelist(self, request):
|
59
|
-
return NoPkDescOrderedChangeList
|
60
|
-
|
61
|
-
def get_ordering(self, request):
|
62
|
-
return None
|
63
|
-
|
64
|
-
def list_to_queryset(self, model, data):
|
65
|
-
from django.db.models.base import ModelBase
|
66
|
-
|
67
|
-
if not isinstance(model, ModelBase):
|
68
|
-
raise ValueError(
|
69
|
-
"%s must be Model" % model
|
70
|
-
)
|
71
|
-
if not isinstance(data, list):
|
72
|
-
raise ValueError(
|
73
|
-
"%s must be List Object" % data
|
74
|
-
)
|
75
|
-
|
76
|
-
pk_list = [obj.pk for obj in data]
|
77
|
-
return model.objects.filter(pk__in=pk_list)
|
78
|
-
|
79
|
-
def _use_treenode_display_mode(self, request, obj):
|
80
|
-
querystring = (request.GET.urlencode() or '')
|
81
|
-
return len(querystring) <= 2
|
82
|
-
|
83
|
-
def _get_treenode_display_mode(self, request, obj):
|
84
|
-
return self.treenode_display_mode
|
85
|
-
|
86
|
-
def _get_treenode_field_default_display(self, obj):
|
87
|
-
return self._get_treenode_field_display_with_breadcrumbs(obj)
|
88
|
-
|
89
|
-
def _get_treenode_field_display(self, request, obj):
|
90
|
-
if not self._use_treenode_display_mode(request, obj):
|
91
|
-
return self._get_treenode_field_default_display(obj)
|
92
|
-
display_mode = self._get_treenode_display_mode(request, obj)
|
93
|
-
if display_mode == TreeNodeModelAdmin.TREENODE_DISPLAY_MODE_ACCORDION:
|
94
|
-
return self._get_treenode_field_display_with_accordion(obj)
|
95
|
-
elif display_mode == TreeNodeModelAdmin.TREENODE_DISPLAY_MODE_BREADCRUMBS:
|
96
|
-
return self._get_treenode_field_display_with_breadcrumbs(obj)
|
97
|
-
elif display_mode == TreeNodeModelAdmin.TREENODE_DISPLAY_MODE_INDENTATION:
|
98
|
-
return self._get_treenode_field_display_with_indentation(obj)
|
99
|
-
else:
|
100
|
-
return self._get_treenode_field_default_display(obj)
|
101
|
-
|
102
|
-
def _get_treenode_field_display_with_accordion(self, obj):
|
103
|
-
tn_namespace = '%s.%s' % (obj.__module__, obj.__class__.__name__, )
|
104
|
-
tn_namespace_key = tn_namespace.lower().replace('.', '_')
|
105
|
-
return mark_safe(''
|
106
|
-
'<span class="treenode"'
|
107
|
-
' data-treenode-type="%s"'
|
108
|
-
' data-treenode-pk="%s"'
|
109
|
-
' data-treenode-accordion="1"'
|
110
|
-
' data-treenode-depth="%s"'
|
111
|
-
' data-treenode-level="%s"'
|
112
|
-
' data-treenode-parent="%s">%s</span>' % (
|
113
|
-
tn_namespace_key,
|
114
|
-
str(obj.pk),
|
115
|
-
str(obj.depth),
|
116
|
-
str(obj.level),
|
117
|
-
str(obj.tn_parent_id or ''),
|
118
|
-
obj.get_display(indent=False), ))
|
119
|
-
|
120
|
-
def _get_treenode_field_display_with_breadcrumbs(self, obj):
|
121
|
-
obj_display = ''
|
122
|
-
for obj_ancestor in obj.get_ancestors():
|
123
|
-
obj_ancestor_display = obj_ancestor.get_display(indent=False)
|
124
|
-
obj_display += '<span class="treenode-breadcrumbs">%s</span>' % (
|
125
|
-
obj_ancestor_display, )
|
126
|
-
obj_display += obj.get_display(indent=False)
|
127
|
-
return mark_safe('<span class="treenode">%s</span>' % (obj_display, ))
|
128
|
-
|
129
|
-
def _get_treenode_field_display_with_indentation(self, obj):
|
130
|
-
obj_display = '<span class="treenode-indentation">—</span>' * obj.ancestors_count
|
131
|
-
obj_display += obj.get_display(indent=False)
|
132
|
-
return mark_safe('<span class="treenode">%s</span>' % (obj_display, ))
|
133
|
-
|
134
|
-
class Media:
|
135
|
-
css = {'all': ('treenode/css/treenode.css',)}
|
136
|
-
js = ['treenode/js/treenode.js']
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
"""
|
3
|
+
TreeNode Admin Module
|
4
|
+
|
5
|
+
"""
|
6
|
+
|
7
|
+
from django.contrib import admin
|
8
|
+
from django.utils.safestring import mark_safe
|
9
|
+
from django.contrib.admin.views.main import ChangeList
|
10
|
+
from .forms import TreeNodeForm
|
11
|
+
|
12
|
+
|
13
|
+
class NoPkDescOrderedChangeList(ChangeList):
|
14
|
+
def get_ordering(self, request, queryset):
|
15
|
+
rv = super().get_ordering(request, queryset)
|
16
|
+
rv = list(rv)
|
17
|
+
rv.remove('-pk') if '-pk' in rv else None
|
18
|
+
return tuple()
|
19
|
+
|
20
|
+
def get_queryset(self, request):
|
21
|
+
qs = self.model.objects.all()
|
22
|
+
return qs.select_related('tn_parent')
|
23
|
+
|
24
|
+
|
25
|
+
class TreeNodeModelAdmin(admin.ModelAdmin):
|
26
|
+
|
27
|
+
TREENODE_DISPLAY_MODE_ACCORDION = 'accordion'
|
28
|
+
TREENODE_DISPLAY_MODE_BREADCRUMBS = 'breadcrumbs'
|
29
|
+
TREENODE_DISPLAY_MODE_INDENTATION = 'indentation'
|
30
|
+
|
31
|
+
treenode_display_mode = TREENODE_DISPLAY_MODE_INDENTATION
|
32
|
+
|
33
|
+
form = TreeNodeForm
|
34
|
+
list_per_page = 1000
|
35
|
+
|
36
|
+
def get_list_display(self, request):
|
37
|
+
base_list_display = super(
|
38
|
+
TreeNodeModelAdmin, self).get_list_display(request)
|
39
|
+
base_list_display = list(base_list_display)
|
40
|
+
|
41
|
+
def treenode_field_display(obj):
|
42
|
+
return self._get_treenode_field_display(request, obj)
|
43
|
+
|
44
|
+
treenode_field_display.short_description = self.model._meta.verbose_name
|
45
|
+
treenode_field_display.allow_tags = True
|
46
|
+
|
47
|
+
if len(base_list_display) == 1 and base_list_display[0] == '__str__':
|
48
|
+
return (treenode_field_display, )
|
49
|
+
else:
|
50
|
+
treenode_display_field = getattr(
|
51
|
+
self.model, 'treenode_display_field')
|
52
|
+
if len(base_list_display) >= 1 and base_list_display[0] == treenode_display_field:
|
53
|
+
base_list_display.pop(0)
|
54
|
+
return (treenode_field_display, ) + tuple(base_list_display)
|
55
|
+
|
56
|
+
return base_list_display
|
57
|
+
|
58
|
+
def get_changelist(self, request):
|
59
|
+
return NoPkDescOrderedChangeList
|
60
|
+
|
61
|
+
def get_ordering(self, request):
|
62
|
+
return None
|
63
|
+
|
64
|
+
def list_to_queryset(self, model, data):
|
65
|
+
from django.db.models.base import ModelBase
|
66
|
+
|
67
|
+
if not isinstance(model, ModelBase):
|
68
|
+
raise ValueError(
|
69
|
+
"%s must be Model" % model
|
70
|
+
)
|
71
|
+
if not isinstance(data, list):
|
72
|
+
raise ValueError(
|
73
|
+
"%s must be List Object" % data
|
74
|
+
)
|
75
|
+
|
76
|
+
pk_list = [obj.pk for obj in data]
|
77
|
+
return model.objects.filter(pk__in=pk_list)
|
78
|
+
|
79
|
+
def _use_treenode_display_mode(self, request, obj):
|
80
|
+
querystring = (request.GET.urlencode() or '')
|
81
|
+
return len(querystring) <= 2
|
82
|
+
|
83
|
+
def _get_treenode_display_mode(self, request, obj):
|
84
|
+
return self.treenode_display_mode
|
85
|
+
|
86
|
+
def _get_treenode_field_default_display(self, obj):
|
87
|
+
return self._get_treenode_field_display_with_breadcrumbs(obj)
|
88
|
+
|
89
|
+
def _get_treenode_field_display(self, request, obj):
|
90
|
+
if not self._use_treenode_display_mode(request, obj):
|
91
|
+
return self._get_treenode_field_default_display(obj)
|
92
|
+
display_mode = self._get_treenode_display_mode(request, obj)
|
93
|
+
if display_mode == TreeNodeModelAdmin.TREENODE_DISPLAY_MODE_ACCORDION:
|
94
|
+
return self._get_treenode_field_display_with_accordion(obj)
|
95
|
+
elif display_mode == TreeNodeModelAdmin.TREENODE_DISPLAY_MODE_BREADCRUMBS:
|
96
|
+
return self._get_treenode_field_display_with_breadcrumbs(obj)
|
97
|
+
elif display_mode == TreeNodeModelAdmin.TREENODE_DISPLAY_MODE_INDENTATION:
|
98
|
+
return self._get_treenode_field_display_with_indentation(obj)
|
99
|
+
else:
|
100
|
+
return self._get_treenode_field_default_display(obj)
|
101
|
+
|
102
|
+
def _get_treenode_field_display_with_accordion(self, obj):
|
103
|
+
tn_namespace = '%s.%s' % (obj.__module__, obj.__class__.__name__, )
|
104
|
+
tn_namespace_key = tn_namespace.lower().replace('.', '_')
|
105
|
+
return mark_safe(''
|
106
|
+
'<span class="treenode"'
|
107
|
+
' data-treenode-type="%s"'
|
108
|
+
' data-treenode-pk="%s"'
|
109
|
+
' data-treenode-accordion="1"'
|
110
|
+
' data-treenode-depth="%s"'
|
111
|
+
' data-treenode-level="%s"'
|
112
|
+
' data-treenode-parent="%s">%s</span>' % (
|
113
|
+
tn_namespace_key,
|
114
|
+
str(obj.pk),
|
115
|
+
str(obj.depth),
|
116
|
+
str(obj.level),
|
117
|
+
str(obj.tn_parent_id or ''),
|
118
|
+
obj.get_display(indent=False), ))
|
119
|
+
|
120
|
+
def _get_treenode_field_display_with_breadcrumbs(self, obj):
|
121
|
+
obj_display = ''
|
122
|
+
for obj_ancestor in obj.get_ancestors():
|
123
|
+
obj_ancestor_display = obj_ancestor.get_display(indent=False)
|
124
|
+
obj_display += '<span class="treenode-breadcrumbs">%s</span>' % (
|
125
|
+
obj_ancestor_display, )
|
126
|
+
obj_display += obj.get_display(indent=False)
|
127
|
+
return mark_safe('<span class="treenode">%s</span>' % (obj_display, ))
|
128
|
+
|
129
|
+
def _get_treenode_field_display_with_indentation(self, obj):
|
130
|
+
obj_display = '<span class="treenode-indentation">—</span>' * obj.ancestors_count
|
131
|
+
obj_display += obj.get_display(indent=False)
|
132
|
+
return mark_safe('<span class="treenode">%s</span>' % (obj_display, ))
|
133
|
+
|
134
|
+
class Media:
|
135
|
+
css = {'all': ('treenode/css/treenode.css',)}
|
136
|
+
js = ['treenode/js/treenode.js']
|
@@ -1,8 +1,8 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
|
3
|
-
import django
|
4
|
-
|
5
|
-
if django.VERSION >= (3, 0):
|
6
|
-
from django.utils.encoding import force_str
|
7
|
-
else:
|
8
|
-
from django.utils.encoding import force_text as force_str
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
import django
|
4
|
+
|
5
|
+
if django.VERSION >= (3, 0):
|
6
|
+
from django.utils.encoding import force_str
|
7
|
+
else:
|
8
|
+
from django.utils.encoding import force_text as force_str
|
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
|
@@ -1,15 +1,15 @@
|
|
1
1
|
# Django-fast-treenode
|
2
2
|
__Combination of Adjacency List and Closure Table__
|
3
3
|
|
4
|
-
##
|
4
|
+
## Features
|
5
5
|
Application for supporting tree (hierarchical) data structure in Django projects
|
6
|
-
*
|
7
|
-
*
|
8
|
-
* compatibility: you can easily add
|
6
|
+
* faster,
|
7
|
+
* synced: in-memory model instances are automatically updated,
|
8
|
+
* compatibility: you can easily add treenode to existing projects,
|
9
9
|
* no dependencies,
|
10
|
-
* easy
|
11
|
-
* admin integration: visualization options (accordion, breadcrumbs or
|
12
|
-
* widget:
|
10
|
+
* easy configuration: just extend the abstract model / model-admin,
|
11
|
+
* admin integration: visualization options (accordion, breadcrumbs or indentation),
|
12
|
+
* widget: build-in Select2-to-Tree extends Select2 to support arbitrary level of nesting.
|
13
13
|
|
14
14
|
## Debut idea
|
15
15
|
This is a modification of the reusable [django-treenode](https://github.com/fabiocaccamo/django-treenode) application developed by [Fabio Caccamo](https://github.com/fabiocaccamo).
|
@@ -44,7 +44,7 @@ You can easily find additional information on your own on the Internet.
|
|
44
44
|
|
45
45
|
## Quick start
|
46
46
|
1. Run ```pip install django-fast-treenode```
|
47
|
-
2. Add ```treenode``` to ```settings.INSTALLED_APPS```
|
47
|
+
2. Add ```django-fast-treenode``` to ```settings.INSTALLED_APPS```
|
48
48
|
3. Make your model inherit from ```treenode.models.TreeNodeModel``` (described below)
|
49
49
|
4. Make your model-admin inherit from ```treenode.admin.TreeNodeModelAdmin``` (described below)
|
50
50
|
5. Run python manage.py makemigrations and ```python manage.py migrate```
|
@@ -157,6 +157,7 @@ class YoursForm(TreeNodeForm):
|
|
157
157
|
- [`get_last_child`](#get_last_child)
|
158
158
|
- [`get_level`](#get_level)
|
159
159
|
- [`get_order`](#get_order)
|
160
|
+
- [`get_ordered_queryset`](#get_ordered_queryset)
|
160
161
|
- [`get_parent`](#get_parent)
|
161
162
|
- [`get_parent_pk`](#get_parent_pk)
|
162
163
|
- [`set_parent`](#set_parent)
|
@@ -360,6 +361,23 @@ obj.get_order()
|
|
360
361
|
obj.order
|
361
362
|
```
|
362
363
|
|
364
|
+
#### `get_ordered_queryset`
|
365
|
+
Returns a queryset of nodes ordered by tn_priority each node.
|
366
|
+
```python
|
367
|
+
cls.get_ordered_queryset()
|
368
|
+
```
|
369
|
+
For example:
|
370
|
+
- A.1
|
371
|
+
- A.1.1
|
372
|
+
- A.1.1.1
|
373
|
+
- A.1.1.2
|
374
|
+
- A.2
|
375
|
+
- A.2.1
|
376
|
+
- ...
|
377
|
+
|
378
|
+
This method uses a lot of memory, ```RawSQL()``` and ```.extra()``` QuerySet method. Use of this method is deprecated due to concerns that Django's ```.extra()``` method **will be deprecated in the future**.
|
379
|
+
Use it only if you cannot otherwise assemble an ordered tree from an Adjacency Table and a Closure Table. In most cases, the data in one Adjacency Table is sufficient for such an assembly. You can easily find the corresponding algorithms (two-pass and one-pass) on the Internet.
|
380
|
+
|
363
381
|
#### `get_parent`
|
364
382
|
Get the **parent node**:
|
365
383
|
```python
|
@@ -561,8 +579,9 @@ Special thanks to [Mathieu Leplatre](https://blog.mathieu-leplatre.info/pages/ab
|
|
561
579
|
|
562
580
|
## To do
|
563
581
|
Future plans:
|
564
|
-
* may be will add the ability to determine the priority of the parent by any field, for example, by creation date or alphabetical order;
|
565
582
|
* drug-and-drop support;
|
583
|
+
* may be will restore caching;
|
584
|
+
* may be will add the ability to determine the priority of the parent by any field, for example, by creation date or alphabetical order;
|
566
585
|
* to be happy, to don't worry, until die.
|
567
586
|
|
568
587
|
|
@@ -1,32 +1,32 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
|
3
|
-
from django import forms
|
4
|
-
from .widgets import TreeWidget
|
5
|
-
|
6
|
-
|
7
|
-
class TreeNodeForm(forms.ModelForm):
|
8
|
-
|
9
|
-
def __init__(self, *args, **kwargs):
|
10
|
-
|
11
|
-
super(TreeNodeForm, self).__init__(*args, **kwargs)
|
12
|
-
|
13
|
-
if 'tn_parent' not in self.fields:
|
14
|
-
return
|
15
|
-
exclude_pks = []
|
16
|
-
obj = self.instance
|
17
|
-
if obj.pk:
|
18
|
-
exclude_pks += [obj.pk]
|
19
|
-
# tn_get_descendants_pks changed to get_descendants_pks()
|
20
|
-
# exclude_pks += split_pks(obj.get_descendants_pks())
|
21
|
-
exclude_pks += obj.get_descendants_pks()
|
22
|
-
|
23
|
-
# Cheaged to "legal" call
|
24
|
-
manager = obj._meta.model.objects
|
25
|
-
|
26
|
-
self.fields['tn_parent'].queryset = manager.prefetch_related(
|
27
|
-
'tn_children').exclude(pk__in=exclude_pks)
|
28
|
-
|
29
|
-
class Meta:
|
30
|
-
widgets = {
|
31
|
-
'tn_parent': TreeWidget(attrs={'style': 'min-width:400px'}),
|
32
|
-
}
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
from django import forms
|
4
|
+
from .widgets import TreeWidget
|
5
|
+
|
6
|
+
|
7
|
+
class TreeNodeForm(forms.ModelForm):
|
8
|
+
|
9
|
+
def __init__(self, *args, **kwargs):
|
10
|
+
|
11
|
+
super(TreeNodeForm, self).__init__(*args, **kwargs)
|
12
|
+
|
13
|
+
if 'tn_parent' not in self.fields:
|
14
|
+
return
|
15
|
+
exclude_pks = []
|
16
|
+
obj = self.instance
|
17
|
+
if obj.pk:
|
18
|
+
exclude_pks += [obj.pk]
|
19
|
+
# tn_get_descendants_pks changed to get_descendants_pks()
|
20
|
+
# exclude_pks += split_pks(obj.get_descendants_pks())
|
21
|
+
exclude_pks += obj.get_descendants_pks()
|
22
|
+
|
23
|
+
# Cheaged to "legal" call
|
24
|
+
manager = obj._meta.model.objects
|
25
|
+
|
26
|
+
self.fields['tn_parent'].queryset = manager.prefetch_related(
|
27
|
+
'tn_children').exclude(pk__in=exclude_pks)
|
28
|
+
|
29
|
+
class Meta:
|
30
|
+
widgets = {
|
31
|
+
'tn_parent': TreeWidget(attrs={'style': 'min-width:400px'}),
|
32
|
+
}
|