django-fast-treenode 3.0.6__py3-none-any.whl → 3.0.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {django_fast_treenode-3.0.6.dist-info → django_fast_treenode-3.0.8.dist-info}/METADATA +1 -1
- {django_fast_treenode-3.0.6.dist-info → django_fast_treenode-3.0.8.dist-info}/RECORD +37 -37
- {django_fast_treenode-3.0.6.dist-info → django_fast_treenode-3.0.8.dist-info}/WHEEL +1 -1
- treenode/__init__.py +1 -0
- treenode/admin/admin.py +105 -109
- treenode/admin/mixin.py +1 -1
- treenode/managers/queries.py +18 -6
- treenode/managers/tasks.py +1 -1
- treenode/models/models.py +2 -2
- treenode/static/css/treenode_admin.css +21 -0
- treenode/static/js/treenode_admin.js +130 -339
- treenode/static/vendors/jquery-ui/AUTHORS.txt +384 -384
- treenode/static/vendors/jquery-ui/LICENSE.txt +43 -43
- treenode/static/vendors/jquery-ui/external/jquery/jquery.js +10716 -10716
- treenode/static/vendors/jquery-ui/index.html +297 -297
- treenode/static/vendors/jquery-ui/jquery-ui.css +438 -438
- treenode/static/vendors/jquery-ui/jquery-ui.js +5222 -5222
- treenode/static/vendors/jquery-ui/jquery-ui.min.css +6 -6
- treenode/static/vendors/jquery-ui/jquery-ui.min.js +5 -5
- treenode/static/vendors/jquery-ui/jquery-ui.structure.css +16 -16
- treenode/static/vendors/jquery-ui/jquery-ui.structure.min.css +4 -4
- treenode/static/vendors/jquery-ui/jquery-ui.theme.css +439 -439
- treenode/static/vendors/jquery-ui/jquery-ui.theme.min.css +4 -4
- treenode/static/vendors/jquery-ui/package.json +82 -82
- treenode/templates/treenode/admin/treenode_changelist.html +61 -15
- treenode/templates/treenode/admin/treenode_rows.html +37 -40
- treenode/templatetags/treenode_admin.py +57 -14
- treenode/utils/db/sqlcompat.py +1 -33
- treenode/utils/db/sqlquery.py +0 -24
- treenode/version.py +2 -2
- treenode/views/autoapi.py +91 -91
- treenode/views/autocomplete.py +52 -52
- treenode/views/children.py +41 -41
- treenode/views/common.py +23 -23
- treenode/widgets.py +0 -2
- {django_fast_treenode-3.0.6.dist-info → django_fast_treenode-3.0.8.dist-info}/licenses/LICENSE +0 -0
- {django_fast_treenode-3.0.6.dist-info → django_fast_treenode-3.0.8.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,5 @@
|
|
1
|
-
django_fast_treenode-3.0.
|
2
|
-
treenode/__init__.py,sha256=
|
1
|
+
django_fast_treenode-3.0.8.dist-info/licenses/LICENSE,sha256=SSYqS84FCnAW7tAxmjBKU8qAa8Jv4VGPuSSGeHwWtJE,1095
|
2
|
+
treenode/__init__.py,sha256=3z1hWpHyy4wg6uz7HCmRi9FaXYeN5CfANVpa77UIoPw,53
|
3
3
|
treenode/apps.py,sha256=QlwjNDM9rkUoWB8Vm8-OkS6lNx0-aTByuGZlu9wrQMs,1832
|
4
4
|
treenode/cache.py,sha256=2jUiiecfFxwB7QFukpU4u0FnDzGH6hNRfo6KAYvs6vM,8447
|
5
5
|
treenode/forms.py,sha256=V-upmbYSW1BbuXdSBGExHxw_j5TTUheaHvreK9tSGTE,3155
|
@@ -7,22 +7,22 @@ treenode/settings.py,sha256=oSkcKXNVd28HXrlWZIH2VYinCMq-UdCDlX4KD0Qc_Xk,631
|
|
7
7
|
treenode/signals.py,sha256=ERrlKjGqhYaPYVKKRk1JBBlPFOmJKpJ6bXsJavcTlo0,518
|
8
8
|
treenode/tests.py,sha256=2uDafv3Ns6f7Vy1ekUtgYxCZEi1KRyesZDTAFhYcX-E,63
|
9
9
|
treenode/urls.py,sha256=krHvVigc_dxC0z5hEd2rgeH6th8jW7qJY3Qbia-419Y,240
|
10
|
-
treenode/version.py,sha256=
|
11
|
-
treenode/widgets.py,sha256=
|
10
|
+
treenode/version.py,sha256=5TH3IchoS9D8YEFnTIlRrjoALxMdemh1PCN0Bb9Jnss,220
|
11
|
+
treenode/widgets.py,sha256=VItPvN9XgaSRI_MZjKEmtaHDJcn2bDIQIppwKjXmYQM,4017
|
12
12
|
treenode/admin/__init__.py,sha256=XNEYHdF5lKb0vpdlVxdR2fxj5oUgzyx1YyCwsv0gxHw,100
|
13
|
-
treenode/admin/admin.py,sha256=
|
13
|
+
treenode/admin/admin.py,sha256=uORuVSx-p0MYXLRVl54cBa7IAL9zicyeYTX3Cv5pBDc,7462
|
14
14
|
treenode/admin/changelist.py,sha256=KUYS9MaR8Ck_1xmMqupobxWKarrJEqmHuEG32CL01Bo,1662
|
15
15
|
treenode/admin/exporter.py,sha256=QE74V6W3tvwA5kCvBt1MmVlLOaWh-o8EU63cgmiwD5Q,5724
|
16
16
|
treenode/admin/importer.py,sha256=hK3D-1DZcoowGblRluGzng3n5Bf__hMsbNaIGXRpRdg,6263
|
17
|
-
treenode/admin/mixin.py,sha256=
|
17
|
+
treenode/admin/mixin.py,sha256=jOkKKt0CpknGokluW8T1YB7VhkCkaAAMYBuptbYDXAw,10648
|
18
18
|
treenode/managers/__init__.py,sha256=c7F9Ku9489Hv6lTpUY2nbyBlWFCXBWAkNBm4xTKcjL8,186
|
19
19
|
treenode/managers/managers.py,sha256=8OaFxtajyR1d7-UHyiUbifMBEF9cjfHTIEYPkYUWmt0,7166
|
20
|
-
treenode/managers/queries.py,sha256=
|
21
|
-
treenode/managers/tasks.py,sha256=
|
20
|
+
treenode/managers/queries.py,sha256=Kepax8SDn7G5tOlPRWBCp5Oyp49O5iMITCMBoNCm_Ak,10655
|
21
|
+
treenode/managers/tasks.py,sha256=nVlGDxNnFlZS1-oU8UH_yTXJERcGrAZ9kCmKwKO5WuY,7138
|
22
22
|
treenode/models/__init__.py,sha256=iR4ksCKoayvkIWWgGk6OUGHZC3D0mzAtgdBcS2vQPBw,188
|
23
23
|
treenode/models/decorators.py,sha256=N2dcnWqSCiEXDcYCf0zVijrbGUC8kYlqOLi_GKFmECU,1457
|
24
24
|
treenode/models/factory.py,sha256=sPUSrvo1za-r6ny3B8ptwevyjO8-iUpPNrT0eSD2kvI,1786
|
25
|
-
treenode/models/models.py,sha256=
|
25
|
+
treenode/models/models.py,sha256=n_2nCJzBOEcmh3r-GCoHV3ZiTX8Xe1ewK9SPptvI_M0,12273
|
26
26
|
treenode/models/mixins/__init__.py,sha256=aALVKMGAWbgMAeKWS6s-NF3L5FmRX96mQxtpthOX-Ec,805
|
27
27
|
treenode/models/mixins/ancestors.py,sha256=9g-0nPHoiF_SX2kN4uDLdbWyw-TDCz1YqxLJngwTZOQ,1971
|
28
28
|
treenode/models/mixins/children.py,sha256=H9iMqgucSmwLX-3O3QUj1a2PUQTmmWZ4GPPjRZ9a5E4,2399
|
@@ -38,25 +38,25 @@ treenode/models/mixins/update.py,sha256=oCZkMnfT23n2n3_mnokDrtTLS_jO_lJRwG3ENkH_
|
|
38
38
|
treenode/static/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
39
39
|
treenode/static/css/.gitkeep,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
40
40
|
treenode/static/css/tree_widget.css,sha256=YVIHuZT_E5ck5N7wvYvuwgV_m-4Ta_uGFiNUoTw-zbk,4861
|
41
|
-
treenode/static/css/treenode_admin.css,sha256=
|
41
|
+
treenode/static/css/treenode_admin.css,sha256=w7yyyf4aFsJKlr_YJPVq6ap5ZvA5Co7ZCYq6ipuV958,3291
|
42
42
|
treenode/static/css/treenode_tabs.css,sha256=pbulaUEhNMQFNWXFQrRN1zMyKjoSqLAnjNygpouPUfE,879
|
43
43
|
treenode/static/js/.gitkeep,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
44
44
|
treenode/static/js/lz-string.min.js,sha256=TAnTJQd2AlLqT9M2TU7GFjnoj9SIfwLeZnpEtLkP624,4718
|
45
45
|
treenode/static/js/tree_widget.js,sha256=2LU8IIJD5TXbWVGjCEgrj5aYnUYtwBZECzj25XLtxNc,9633
|
46
|
-
treenode/static/js/treenode_admin.js,sha256=
|
47
|
-
treenode/static/vendors/jquery-ui/AUTHORS.txt,sha256=
|
48
|
-
treenode/static/vendors/jquery-ui/LICENSE.txt,sha256=
|
49
|
-
treenode/static/vendors/jquery-ui/index.html,sha256=
|
50
|
-
treenode/static/vendors/jquery-ui/jquery-ui.css,sha256=
|
51
|
-
treenode/static/vendors/jquery-ui/jquery-ui.js,sha256=
|
52
|
-
treenode/static/vendors/jquery-ui/jquery-ui.min.css,sha256=
|
53
|
-
treenode/static/vendors/jquery-ui/jquery-ui.min.js,sha256=
|
54
|
-
treenode/static/vendors/jquery-ui/jquery-ui.structure.css,sha256=
|
55
|
-
treenode/static/vendors/jquery-ui/jquery-ui.structure.min.css,sha256=
|
56
|
-
treenode/static/vendors/jquery-ui/jquery-ui.theme.css,sha256=
|
57
|
-
treenode/static/vendors/jquery-ui/jquery-ui.theme.min.css,sha256=
|
58
|
-
treenode/static/vendors/jquery-ui/package.json,sha256=
|
59
|
-
treenode/static/vendors/jquery-ui/external/jquery/jquery.js,sha256=
|
46
|
+
treenode/static/js/treenode_admin.js,sha256=IU3Zj2Q-WBxftl0zsjeRPrt1xOzzdEKlzGEMThi3hrk,9706
|
47
|
+
treenode/static/vendors/jquery-ui/AUTHORS.txt,sha256=geZ7JfVsTJq8Wgjrcm1p56Zqjxx7PL9FkxhKiN8dQg8,14848
|
48
|
+
treenode/static/vendors/jquery-ui/LICENSE.txt,sha256=9kkCXPZcjc2FiLCTeIO6elK9ttNRKUZKNqFgLrACUPE,1818
|
49
|
+
treenode/static/vendors/jquery-ui/index.html,sha256=RSv8cA662qJdc02OX-UmpaBJoZrngoQqnNWVZNn9GQQ,24229
|
50
|
+
treenode/static/vendors/jquery-ui/jquery-ui.css,sha256=O8mmheetXPeKUa-iDk8fdxDhs096eWIi10qLJb6OXyw,17171
|
51
|
+
treenode/static/vendors/jquery-ui/jquery-ui.js,sha256=XtnlPiKuVzMh-cef76mBCNCTPqk2cIlKBgmTHfRBDog,146430
|
52
|
+
treenode/static/vendors/jquery-ui/jquery-ui.min.css,sha256=Yv2MureLOGZicbgEv28X9ft9o0SRBZ84PWIgg6h_V2E,15003
|
53
|
+
treenode/static/vendors/jquery-ui/jquery-ui.min.js,sha256=FT6F7DDm5J1Qz6B5fv3e8jnmx9QyyfR2qUeTptb73bA,70364
|
54
|
+
treenode/static/vendors/jquery-ui/jquery-ui.structure.css,sha256=392jP1trXFqS1OVv2X50px0F8w1DeTyIvvHL9wxNXi0,328
|
55
|
+
treenode/static/vendors/jquery-ui/jquery-ui.structure.min.css,sha256=cXLe7UDVCN5SvPpbb3HJdndQIk1hlWMrzyRRYO9rcLs,208
|
56
|
+
treenode/static/vendors/jquery-ui/jquery-ui.theme.css,sha256=grsB4hVvwiBxnNsG1c_ocH1YK76CExOav_dn_BsxepU,17140
|
57
|
+
treenode/static/vendors/jquery-ui/jquery-ui.theme.min.css,sha256=oW6e3DmGiKSNLzo3HCUnjes9ykGZ6gXEtl3LjiQIkMs,13708
|
58
|
+
treenode/static/vendors/jquery-ui/package.json,sha256=AvCATruWzbwY7NsfzcUR2WqPjslGO8M7hHzTj1AYDQY,2122
|
59
|
+
treenode/static/vendors/jquery-ui/external/jquery/jquery.js,sha256=eKhayi8LEQwp4NKxN-CfCh-3qOVUtJn3QNZ0TciWLP4,285314
|
60
60
|
treenode/static/vendors/jquery-ui/images/ui-icons_444444_256x240.png,sha256=YW09ms2dyfoVEOT_NEpaWXMMRLJxVIqu8hku63SoCEA,7107
|
61
61
|
treenode/static/vendors/jquery-ui/images/ui-icons_555555_256x240.png,sha256=HP6KoU5aOllbVtaudhmNOLhBIzi_0oCuxBjwyhDATOo,7105
|
62
62
|
treenode/static/vendors/jquery-ui/images/ui-icons_777620_256x240.png,sha256=NxKGIMNmb6YYZor0RuijQ7PTK5BoA9b5qXLjYTMFvXo,4615
|
@@ -67,27 +67,27 @@ treenode/templates/.gitkeep,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
|
67
67
|
treenode/templates/treenode/.gitkeep,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
68
68
|
treenode/templates/treenode/admin/.gitkeep,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
69
69
|
treenode/templates/treenode/admin/treenode_ajax_rows.html,sha256=zFyPaTbSyxRjOqQ85SMv__qTIYDjEna6chYODBypDZA,224
|
70
|
-
treenode/templates/treenode/admin/treenode_changelist.html,sha256=
|
70
|
+
treenode/templates/treenode/admin/treenode_changelist.html,sha256=JiUwX33w5WwegX7gM2ctw-bx9XOyXwKt9QB_6g7Dfz8,2283
|
71
71
|
treenode/templates/treenode/admin/treenode_import_export.html,sha256=K6L_JnhGpWgQdduXSfAJ0UuTBfSN4ylFw9w_e5tPDDE,2926
|
72
|
-
treenode/templates/treenode/admin/treenode_rows.html,sha256=
|
72
|
+
treenode/templates/treenode/admin/treenode_rows.html,sha256=de90wKpNdVB2C5CGnIkStBB0dWCDkmWr_pluV_NgBUs,1857
|
73
73
|
treenode/templates/treenode/widgets/tree_widget.html,sha256=GKcCU-B2FkkJ2BSOuXOw9e_PdYTtADcvyITEXqOlZ9Y,723
|
74
74
|
treenode/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
75
|
-
treenode/templatetags/treenode_admin.py,sha256=
|
75
|
+
treenode/templatetags/treenode_admin.py,sha256=5f5oqAS4zC_f0kkJRsm5MqXHjsKJXn8GslbZcbMivxg,2736
|
76
76
|
treenode/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
77
77
|
treenode/utils/db/__init__.py,sha256=RwicAcJSI1nhIPWLdT7j9TFsgOc9834VDn9lVn54GlY,247
|
78
78
|
treenode/utils/db/compiler.py,sha256=PgD9ybS5H8OUHw1gkFBQHhnrf5HiCx8QXUMRhydwh7o,3824
|
79
79
|
treenode/utils/db/db_vendor.py,sha256=4SyEHl51jVCDB3is4omHCf2bTB_QV3RvemUQYxJP5m0,930
|
80
80
|
treenode/utils/db/service.py,sha256=PF85Yhz2xUWFFCzpLYotmiNTZXXEH61rhswslSxEUds,2640
|
81
|
-
treenode/utils/db/sqlcompat.py,sha256=
|
82
|
-
treenode/utils/db/sqlquery.py,sha256=
|
81
|
+
treenode/utils/db/sqlcompat.py,sha256=K71ggkKIvpdTtHQ6Y4qcbo6cj2eYiEfy6DlVBr8Po1E,4460
|
82
|
+
treenode/utils/db/sqlquery.py,sha256=KXcfKbbaBF-D134H_2DiPQtjedR79SJNXPJc0msZYEc,1938
|
83
83
|
treenode/views/__init__.py,sha256=ppxbBx51TUaKstJFpAd_DTmbKjbZGmVMLNYSpgUKnd0,111
|
84
|
-
treenode/views/autoapi.py,sha256=
|
85
|
-
treenode/views/autocomplete.py,sha256=
|
86
|
-
treenode/views/children.py,sha256=
|
87
|
-
treenode/views/common.py,sha256=
|
84
|
+
treenode/views/autoapi.py,sha256=IAQfhWey4z-QBuAn-QDHhpJSFCF702sHtqYOA6vK4BY,3537
|
85
|
+
treenode/views/autocomplete.py,sha256=ERGJT4jfCE2GUv94Ja8SgJyG1FOAyqIeB0PKH1fyU7g,1417
|
86
|
+
treenode/views/children.py,sha256=seO0SNKriRY2FJOO1oZgX42iKolXMf3EF76GrfdJZLQ,1111
|
87
|
+
treenode/views/common.py,sha256=kUN3IgMZCdNdJ2haxB9MTGcn2rctyFUAHNagzcu9wXk,594
|
88
88
|
treenode/views/crud.py,sha256=RI5rdyD4hZTszjZFThByxi_lkAeJlqbDCXFkD8iyzKE,7424
|
89
89
|
treenode/views/search.py,sha256=c_GyooT3jyoNa96bBxfoWruRN1wIw-ZGYvwGKkGojTs,1501
|
90
|
-
django_fast_treenode-3.0.
|
91
|
-
django_fast_treenode-3.0.
|
92
|
-
django_fast_treenode-3.0.
|
93
|
-
django_fast_treenode-3.0.
|
90
|
+
django_fast_treenode-3.0.8.dist-info/METADATA,sha256=-DOMkpuNAIruYNiRcCc7g6zGVpbWjter7IEDb9k10mg,10249
|
91
|
+
django_fast_treenode-3.0.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
92
|
+
django_fast_treenode-3.0.8.dist-info/top_level.txt,sha256=fmgxHbXyx1O2MPi_9kjx8aL9L-8TmV0gre4Go8XgqFk,9
|
93
|
+
django_fast_treenode-3.0.8.dist-info/RECORD,,
|
treenode/__init__.py
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
default_app_config = 'treenode.apps.TreeNodeConfig'
|
treenode/admin/admin.py
CHANGED
@@ -2,7 +2,26 @@
|
|
2
2
|
"""
|
3
3
|
TreeNode Admin Model Class
|
4
4
|
|
5
|
-
|
5
|
+
Modified admin panel for django-fast-treenode. Solves the following problems:
|
6
|
+
- Set list_per_page = 10000 to display all elements at once.
|
7
|
+
- Hidden standard pagination via CSS
|
8
|
+
- Disabled counting the total number of elements to speed up loading
|
9
|
+
- Accordion works regardless of the display mode
|
10
|
+
Two modes are supported:
|
11
|
+
- Indented - with indents and icons
|
12
|
+
- Breadcrumbs - with breadcrumbs
|
13
|
+
All modes have links to editing objects.
|
14
|
+
|
15
|
+
- Expand buttons for nodes with children
|
16
|
+
|
17
|
+
Additional features:
|
18
|
+
- Control panel with "Expand All" / "Collapse All" buttons
|
19
|
+
- Saving the state of the tree between page transitions
|
20
|
+
- Smooth animations when expanding/collapsing
|
21
|
+
- Counting the total number of nodes in the tree
|
22
|
+
- Recursive hiding of grandchildren when collapsing the parent
|
23
|
+
|
24
|
+
Version: 3.1.0
|
6
25
|
Author: Timur Kady
|
7
26
|
Email: timurkady@yandex.com
|
8
27
|
"""
|
@@ -10,12 +29,11 @@ Email: timurkady@yandex.com
|
|
10
29
|
|
11
30
|
from django.contrib import admin
|
12
31
|
from django.db import models
|
13
|
-
from django.http import HttpResponseRedirect
|
14
32
|
from django.urls import reverse
|
33
|
+
from django.utils.html import escape
|
15
34
|
from django.utils.safestring import mark_safe
|
16
35
|
from django.utils.translation import gettext_lazy as _
|
17
36
|
|
18
|
-
from .changelist import TreeNodeChangeList
|
19
37
|
from .mixin import AdminMixin
|
20
38
|
from ..forms import TreeNodeForm
|
21
39
|
from ..widgets import TreeWidget
|
@@ -33,14 +51,12 @@ class TreeNodeModelAdmin(AdminMixin, admin.ModelAdmin):
|
|
33
51
|
# Режимы отображения
|
34
52
|
TREENODE_DISPLAY_MODE_ACCORDION = 'accordion'
|
35
53
|
TREENODE_DISPLAY_MODE_BREADCRUMBS = 'breadcrumbs'
|
36
|
-
TREENODE_DISPLAY_MODE_INDENTATION = 'indentation'
|
37
54
|
treenode_display_mode = TREENODE_DISPLAY_MODE_ACCORDION
|
38
55
|
|
39
56
|
form = TreeNodeForm
|
40
57
|
importer_class = None
|
41
58
|
exporter_class = None
|
42
59
|
ordering = []
|
43
|
-
list_per_page = 1000
|
44
60
|
|
45
61
|
formfield_overrides = {
|
46
62
|
models.ForeignKey: {'widget': TreeWidget()},
|
@@ -88,79 +104,69 @@ class TreeNodeModelAdmin(AdminMixin, admin.ModelAdmin):
|
|
88
104
|
|
89
105
|
toggle.short_description = _("Expand")
|
90
106
|
|
91
|
-
def
|
92
|
-
"""
|
93
|
-
|
94
|
-
edit_url = reverse(
|
95
|
-
f"admin:{obj._meta.app_label}_{obj._meta.model_name}_change",
|
96
|
-
args=[obj.pk]
|
97
|
-
)
|
107
|
+
def get_changelist(self, request, **kwargs):
|
108
|
+
"""Get changelist."""
|
109
|
+
ChangeList = super().get_changelist(request, **kwargs)
|
98
110
|
|
99
|
-
|
100
|
-
|
101
|
-
content = (
|
102
|
-
f'<span style="padding-left: {level * 1.5}em;">'
|
103
|
-
f'{icon}<a href="{edit_url}">{str(obj)}</a></span>'
|
104
|
-
)
|
105
|
-
elif self.treenode_display_mode == self.TREENODE_DISPLAY_MODE_BREADCRUMBS: # noqa
|
106
|
-
breadcrumbs = obj.get_breadcrumbs(
|
107
|
-
attr=getattr(obj, 'treenode_display_field', 'id'))
|
108
|
-
content = " / ".join(map(str, breadcrumbs))
|
109
|
-
elif self.treenode_display_mode == self.TREENODE_DISPLAY_MODE_INDENTATION: # noqa
|
110
|
-
indent = "—" * level
|
111
|
-
content = f'{indent}<a href="{edit_url}">{str(obj)}</a>'
|
112
|
-
else:
|
113
|
-
content = f'<a href="{edit_url}">{str(obj)}</a>'
|
114
|
-
|
115
|
-
html = (
|
116
|
-
f'<div class="treenode-wrapper" '
|
117
|
-
f'data-treenode-pk="{obj.pk}" '
|
118
|
-
f'data-treenode-depth="{level}" '
|
119
|
-
f'data-treenode-parent="{obj.parent_id or ""}">'
|
120
|
-
f'<span class="treenode-content">{content}</span>'
|
121
|
-
f'</div>'
|
122
|
-
)
|
123
|
-
return mark_safe(html)
|
111
|
+
class NoPaginationChangeList(ChangeList):
|
112
|
+
"""Suppress pagination."""
|
124
113
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
114
|
+
def get_results(self, request):
|
115
|
+
"""Get result."""
|
116
|
+
super().get_results(request)
|
117
|
+
self.paginator.show_all = True
|
118
|
+
self.result_count = len(self.result_list)
|
119
|
+
self.full_result_count = len(self.result_list)
|
120
|
+
self.can_show_all = False
|
121
|
+
self.multi_page = False
|
122
|
+
self.actions = self.model_admin.get_actions(request)
|
131
123
|
|
132
|
-
|
124
|
+
return NoPaginationChangeList
|
133
125
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
user_list_display = [treenode_field]
|
138
|
-
else:
|
139
|
-
try:
|
140
|
-
pos = user_list_display.index(display_field)
|
141
|
-
user_list_display.pop(pos)
|
142
|
-
user_list_display.insert(pos, treenode_field)
|
143
|
-
except ValueError:
|
144
|
-
user_list_display.insert(0, treenode_field)
|
126
|
+
def get_changelist_instance(self, request):
|
127
|
+
"""
|
128
|
+
Get changelist instance.
|
145
129
|
|
146
|
-
|
130
|
+
Make sure our custom ChangeList is used without pagination.
|
131
|
+
"""
|
132
|
+
ChangeList = self.get_changelist(request)
|
147
133
|
|
148
|
-
|
149
|
-
|
150
|
-
|
134
|
+
return ChangeList(
|
135
|
+
request,
|
136
|
+
self.model,
|
137
|
+
self.get_list_display(request),
|
138
|
+
self.get_list_display_links(
|
139
|
+
request,
|
140
|
+
self.get_list_display(request)
|
141
|
+
),
|
142
|
+
self.get_list_filter(request),
|
143
|
+
self.date_hierarchy,
|
144
|
+
self.search_fields,
|
145
|
+
self.list_select_related,
|
146
|
+
self.list_per_page,
|
147
|
+
self.list_max_show_all,
|
148
|
+
self.list_editable,
|
149
|
+
self,
|
150
|
+
sortable_by=self.get_sortable_by(request),
|
151
|
+
search_help_text=self.get_search_help_text(request),
|
152
|
+
)
|
151
153
|
|
152
154
|
def get_queryset(self, request):
|
153
|
-
"""
|
155
|
+
"""Get queryset."""
|
154
156
|
qs = super().get_queryset(request)
|
157
|
+
return qs.select_related('parent')\
|
158
|
+
.prefetch_related('children')\
|
159
|
+
.order_by('_path')
|
155
160
|
|
156
|
-
|
157
|
-
|
161
|
+
def get_list_display(self, request):
|
162
|
+
"""Get list_display."""
|
163
|
+
def treenode_field(obj):
|
164
|
+
return self._get_treenode_field_display(request, obj)
|
158
165
|
|
159
|
-
|
160
|
-
|
161
|
-
return qs
|
166
|
+
description = str(self.model._meta.verbose_name)
|
167
|
+
treenode_field.short_description = description
|
162
168
|
|
163
|
-
return
|
169
|
+
return (self.drag, self.toggle, treenode_field)
|
164
170
|
|
165
171
|
def get_form(self, request, obj=None, **kwargs):
|
166
172
|
"""Get Form method."""
|
@@ -171,56 +177,46 @@ class TreeNodeModelAdmin(AdminMixin, admin.ModelAdmin):
|
|
171
177
|
|
172
178
|
def get_search_fields(self, request):
|
173
179
|
"""Get search fields."""
|
174
|
-
return [getattr(self.model, '
|
175
|
-
|
176
|
-
def get_changelist(self, request, **kwargs):
|
177
|
-
"""Get ChangeList Class."""
|
178
|
-
return TreeNodeChangeList
|
179
|
-
|
180
|
-
def get_ordering(self, request):
|
181
|
-
"""Get ordering."""
|
182
|
-
return None
|
183
|
-
|
184
|
-
def changelist_view(self, request, extra_context=None):
|
185
|
-
"""Changelist View."""
|
186
|
-
extra_context = extra_context or {}
|
187
|
-
# TODO
|
188
|
-
extra_context['import_export_enabled'] = self.import_export
|
189
|
-
extra_context['num_sorted_fields'] = len(self.get_ordering(request) or []) # noqa: D501
|
190
|
-
|
191
|
-
response = super().changelist_view(request, extra_context=extra_context)
|
192
|
-
|
193
|
-
# If response is a redirect, then there is no point in updating
|
194
|
-
# ChangeList
|
195
|
-
if isinstance(response, HttpResponseRedirect):
|
196
|
-
return response
|
180
|
+
return [getattr(self.model, 'display_field', 'id') or 'id']
|
197
181
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
self.get_sortable_by(request),
|
213
|
-
self.get_search_help_text(request),
|
182
|
+
def _get_treenode_field_display(self, request, obj):
|
183
|
+
"""
|
184
|
+
Generate HTML to display tree nodes.
|
185
|
+
|
186
|
+
Depending on the selected display mode (accordion or breadcrumbs),
|
187
|
+
do the following:
|
188
|
+
- For accordion mode: add indents and icons.
|
189
|
+
- For breadcrumbs mode: display breadcrumb path.
|
190
|
+
"""
|
191
|
+
level = obj.get_depth()
|
192
|
+
display_field = getattr(obj, "display_field", None)
|
193
|
+
edit_url = reverse(
|
194
|
+
f"admin:{obj._meta.app_label}_{obj._meta.model_name}_change",
|
195
|
+
args=[obj.pk]
|
214
196
|
)
|
197
|
+
icon = ""
|
198
|
+
text = ""
|
199
|
+
padding = ""
|
200
|
+
closing = ""
|
201
|
+
|
202
|
+
if self.treenode_display_mode == self.TREENODE_DISPLAY_MODE_ACCORDION:
|
203
|
+
icon = "📄 " if obj.is_leaf() else "📁 "
|
204
|
+
text = getattr(obj, display_field, str(obj))
|
205
|
+
padding = f'<span style="padding-left: {level * 1.5}em;">'
|
206
|
+
closing = "</span>"
|
207
|
+
elif self.treenode_display_mode == self.TREENODE_DISPLAY_MODE_BREADCRUMBS: # noqa
|
208
|
+
if display_field:
|
209
|
+
breadcrumbs = obj.get_breadcrumbs(attr=display_field)
|
210
|
+
else:
|
211
|
+
breadcrumbs = [str(item) for item in obj.get_ancestors()]
|
215
212
|
|
216
|
-
|
217
|
-
cl.result_list = self.render_changelist_rows(cl.result_list, request)
|
213
|
+
text = "/" + "/".join([escape(label) for label in breadcrumbs])
|
218
214
|
|
219
|
-
|
215
|
+
content = f'{padding}{icon}<a href="{edit_url}">{escape(text)}</a>{closing}' # noqa
|
216
|
+
return mark_safe(content)
|
220
217
|
|
221
218
|
def get_list_per_page(self, request):
|
222
219
|
"""Get list per page."""
|
223
220
|
return 999999
|
224
221
|
|
225
|
-
|
226
222
|
# The End
|
treenode/admin/mixin.py
CHANGED
treenode/managers/queries.py
CHANGED
@@ -5,14 +5,13 @@ Low-level SQL Query Manager.
|
|
5
5
|
Encapsulates all logic to retrieve related primary keys based on relationships
|
6
6
|
(e.g., ancestors, children, descendants, siblings, family, root) using raw SQL.
|
7
7
|
|
8
|
-
Version: 3.0.
|
8
|
+
Version: 3.0.0
|
9
9
|
Author: Timur Kady
|
10
10
|
Email: timurkady@yandex.com
|
11
11
|
"""
|
12
12
|
|
13
13
|
|
14
14
|
from django.db import connection
|
15
|
-
from ..utils.db.sqlcompat import SQLCompat
|
16
15
|
|
17
16
|
|
18
17
|
class TreeQuery:
|
@@ -33,6 +32,19 @@ class TreeQuery:
|
|
33
32
|
cursor.execute(sql, params)
|
34
33
|
return cursor.fetchall()
|
35
34
|
|
35
|
+
def wrap_union_all(self, queries):
|
36
|
+
"""
|
37
|
+
Combine multiple SQL queries using UNION ALL.
|
38
|
+
|
39
|
+
Each query is a tuple: (sql, params).
|
40
|
+
Returns a tuple: (combined_sql, combined_params).
|
41
|
+
"""
|
42
|
+
union_query = " UNION ALL ".join(f"({q[0]})" for q in queries)
|
43
|
+
combined_params = []
|
44
|
+
for q in queries:
|
45
|
+
combined_params.extend(q[1])
|
46
|
+
return union_query, combined_params
|
47
|
+
|
36
48
|
def order_by(self, sql, order_by_clause):
|
37
49
|
"""Wrap the SQL in an outer query to enforce ordering."""
|
38
50
|
return f"SELECT * FROM ({sql}) AS combined ORDER BY {order_by_clause}"
|
@@ -64,7 +76,7 @@ class TreeQuery:
|
|
64
76
|
if include_self:
|
65
77
|
sql2 = f"SELECT id, priority FROM {self.db_table} WHERE id = %s"
|
66
78
|
params2 = [self.node.pk]
|
67
|
-
combined_sql, combined_params =
|
79
|
+
combined_sql, combined_params = self.wrap_union_all(
|
68
80
|
[(sql1, params1), (sql2, params2)])
|
69
81
|
sql = self.order_by(combined_sql, "priority")
|
70
82
|
return sql, combined_params
|
@@ -103,7 +115,7 @@ class TreeQuery:
|
|
103
115
|
FROM {self.db_table}
|
104
116
|
WHERE id = %s
|
105
117
|
"""
|
106
|
-
union_sql, union_params =
|
118
|
+
union_sql, union_params = self.wrap_union_all([
|
107
119
|
(base_sql, params),
|
108
120
|
(sql_self, [self.node.pk])
|
109
121
|
])
|
@@ -136,7 +148,7 @@ class TreeQuery:
|
|
136
148
|
|
137
149
|
if include_self:
|
138
150
|
sql_self = f"SELECT id, _depth, priority FROM {self.db_table} WHERE id = %s" # noqa: D501
|
139
|
-
union_sql, union_params =
|
151
|
+
union_sql, union_params = self.wrap_union_all(
|
140
152
|
[(base_sql, params), (sql_self, [self.node.pk])])
|
141
153
|
else:
|
142
154
|
union_sql, union_params = base_sql, params
|
@@ -186,7 +198,7 @@ class TreeQuery:
|
|
186
198
|
if include_self:
|
187
199
|
sql_self = f"SELECT id, _depth, priority FROM {self.db_table} WHERE id = %s" # noqa: D501
|
188
200
|
queries.append((sql_self, [self.node.pk]))
|
189
|
-
combined_sql, combined_params =
|
201
|
+
combined_sql, combined_params = self.wrap_union_all(queries)
|
190
202
|
combined_sql = self.order_by(combined_sql, "_depth, priority")
|
191
203
|
return combined_sql, combined_params
|
192
204
|
|
treenode/managers/tasks.py
CHANGED
treenode/models/models.py
CHANGED
@@ -14,7 +14,7 @@ Features:
|
|
14
14
|
- Provides a caching mechanism to optimize performance.
|
15
15
|
- Includes methods for tree traversal, manipulation, and serialization.
|
16
16
|
|
17
|
-
Version: 3.0.
|
17
|
+
Version: 3.0.7
|
18
18
|
Author: Timur Kady
|
19
19
|
Email: timurkady@yandex.com
|
20
20
|
|
@@ -122,7 +122,7 @@ class TreeNodeModel(
|
|
122
122
|
|
123
123
|
def __str__(self):
|
124
124
|
"""Return a human-readable string representation of an object."""
|
125
|
-
field = getattr(
|
125
|
+
field = getattr(self, 'display_field', None)
|
126
126
|
if field and hasattr(self, field):
|
127
127
|
return str(getattr(self, field))
|
128
128
|
return f'Node {self.pk}'
|
@@ -66,6 +66,27 @@ Email: timurkady@yandex.com
|
|
66
66
|
opacity: 1.0;
|
67
67
|
}
|
68
68
|
|
69
|
+
.treenode-toolbar{
|
70
|
+
display: flex;
|
71
|
+
}
|
72
|
+
|
73
|
+
.treenode-toolbar {
|
74
|
+
margin: 15px 0px;
|
75
|
+
}
|
76
|
+
|
77
|
+
.treenode-button {
|
78
|
+
padding: 5px !important;
|
79
|
+
margin-left: 15px !important;
|
80
|
+
}
|
81
|
+
|
82
|
+
tr.treenode-hidden {
|
83
|
+
display: none;
|
84
|
+
}
|
85
|
+
|
86
|
+
td.action-checkbox{
|
87
|
+
text-align: center;
|
88
|
+
}
|
89
|
+
|
69
90
|
.dark-theme .treenode-toggle {
|
70
91
|
color: #ccc;
|
71
92
|
background-color: #444;
|