django-fast-treenode 3.0.8__py3-none-any.whl → 3.2.0__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.
Files changed (47) hide show
  1. {django_fast_treenode-3.0.8.dist-info → django_fast_treenode-3.2.0.dist-info}/METADATA +3 -1
  2. django_fast_treenode-3.2.0.dist-info/RECORD +97 -0
  3. treenode/admin/admin.py +5 -5
  4. treenode/admin/mixin.py +6 -6
  5. treenode/managers/managers.py +1 -1
  6. treenode/models/mixins/node.py +4 -4
  7. treenode/models/models.py +14 -9
  8. treenode/settings.py +4 -1
  9. treenode/static/treenode/.gitkeep +0 -0
  10. treenode/static/treenode/vendors/.gitkeep +0 -0
  11. treenode/static/treenode/vendors/jquery-ui/.gitkeep +0 -0
  12. treenode/templates/treenode/admin/treenode_import_export.html +3 -2
  13. treenode/utils/jwt_auth.py +25 -0
  14. treenode/version.py +2 -2
  15. treenode/views/autoapi.py +95 -91
  16. treenode/widgets.py +2 -2
  17. django_fast_treenode-3.0.8.dist-info/RECORD +0 -93
  18. {django_fast_treenode-3.0.8.dist-info → django_fast_treenode-3.2.0.dist-info}/WHEEL +0 -0
  19. {django_fast_treenode-3.0.8.dist-info → django_fast_treenode-3.2.0.dist-info}/licenses/LICENSE +0 -0
  20. {django_fast_treenode-3.0.8.dist-info → django_fast_treenode-3.2.0.dist-info}/top_level.txt +0 -0
  21. /treenode/static/{css → treenode/css}/.gitkeep +0 -0
  22. /treenode/static/{css → treenode/css}/tree_widget.css +0 -0
  23. /treenode/static/{css → treenode/css}/treenode_admin.css +0 -0
  24. /treenode/static/{css → treenode/css}/treenode_tabs.css +0 -0
  25. /treenode/static/{js → treenode/js}/.gitkeep +0 -0
  26. /treenode/static/{js → treenode/js}/lz-string.min.js +0 -0
  27. /treenode/static/{js → treenode/js}/tree_widget.js +0 -0
  28. /treenode/static/{js → treenode/js}/treenode_admin.js +0 -0
  29. /treenode/static/{vendors → treenode/vendors}/jquery-ui/AUTHORS.txt +0 -0
  30. /treenode/static/{vendors → treenode/vendors}/jquery-ui/LICENSE.txt +0 -0
  31. /treenode/static/{vendors → treenode/vendors}/jquery-ui/external/jquery/jquery.js +0 -0
  32. /treenode/static/{vendors → treenode/vendors}/jquery-ui/images/ui-icons_444444_256x240.png +0 -0
  33. /treenode/static/{vendors → treenode/vendors}/jquery-ui/images/ui-icons_555555_256x240.png +0 -0
  34. /treenode/static/{vendors → treenode/vendors}/jquery-ui/images/ui-icons_777620_256x240.png +0 -0
  35. /treenode/static/{vendors → treenode/vendors}/jquery-ui/images/ui-icons_777777_256x240.png +0 -0
  36. /treenode/static/{vendors → treenode/vendors}/jquery-ui/images/ui-icons_cc0000_256x240.png +0 -0
  37. /treenode/static/{vendors → treenode/vendors}/jquery-ui/images/ui-icons_ffffff_256x240.png +0 -0
  38. /treenode/static/{vendors → treenode/vendors}/jquery-ui/index.html +0 -0
  39. /treenode/static/{vendors → treenode/vendors}/jquery-ui/jquery-ui.css +0 -0
  40. /treenode/static/{vendors → treenode/vendors}/jquery-ui/jquery-ui.js +0 -0
  41. /treenode/static/{vendors → treenode/vendors}/jquery-ui/jquery-ui.min.css +0 -0
  42. /treenode/static/{vendors → treenode/vendors}/jquery-ui/jquery-ui.min.js +0 -0
  43. /treenode/static/{vendors → treenode/vendors}/jquery-ui/jquery-ui.structure.css +0 -0
  44. /treenode/static/{vendors → treenode/vendors}/jquery-ui/jquery-ui.structure.min.css +0 -0
  45. /treenode/static/{vendors → treenode/vendors}/jquery-ui/jquery-ui.theme.css +0 -0
  46. /treenode/static/{vendors → treenode/vendors}/jquery-ui/jquery-ui.theme.min.css +0 -0
  47. /treenode/static/{vendors → treenode/vendors}/jquery-ui/package.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-fast-treenode
3
- Version: 3.0.8
3
+ Version: 3.2.0
4
4
  Summary: Treenode Framework for supporting tree (hierarchical) data structure in Django projects
5
5
  Home-page: https://django-fast-treenode.readthedocs.io/
6
6
  Author: Timur Kady
@@ -57,6 +57,7 @@ Requires-Dist: Django>=5.0
57
57
  Requires-Dist: msgpack>=1.0.0
58
58
  Requires-Dist: openpyxl>=3.0.0
59
59
  Requires-Dist: pyyaml>=5.1
60
+ Requires-Dist: PyJWT>=2.0
60
61
  Dynamic: author
61
62
  Dynamic: home-page
62
63
  Dynamic: license-file
@@ -126,6 +127,7 @@ At the moment, django-fast-treeenode is, if not the best, then one of the best p
126
127
  - **Convenient administration**: the admin panel interface was developed taking into account the experience of using other packages. It provides convenience and intuitiveness with ease of programming.
127
128
  - **Scalability**: **Treenode Framework** suitable for solving simple problems such as menus, directories, parsing arithmetic expressions, as well as complex problems such as program optimization, image layout, multi-step decision making problems, or machine learning..
128
129
  - **Lightweight**: All functionality is implemented within the package without heavyweight dependencies such as `djangorestframework` or `django-import-export`.
130
+ - **Optional JWT authentication**: enable token-based protection for the API with a single setting.
129
131
 
130
132
  All this makes **Treenode Framework** a prime candidate for your needs.
131
133
 
@@ -0,0 +1,97 @@
1
+ django_fast_treenode-3.2.0.dist-info/licenses/LICENSE,sha256=SSYqS84FCnAW7tAxmjBKU8qAa8Jv4VGPuSSGeHwWtJE,1095
2
+ treenode/__init__.py,sha256=3z1hWpHyy4wg6uz7HCmRi9FaXYeN5CfANVpa77UIoPw,53
3
+ treenode/apps.py,sha256=QlwjNDM9rkUoWB8Vm8-OkS6lNx0-aTByuGZlu9wrQMs,1832
4
+ treenode/cache.py,sha256=2jUiiecfFxwB7QFukpU4u0FnDzGH6hNRfo6KAYvs6vM,8447
5
+ treenode/forms.py,sha256=V-upmbYSW1BbuXdSBGExHxw_j5TTUheaHvreK9tSGTE,3155
6
+ treenode/settings.py,sha256=FRGK7hl_Tnxp4sGbUNxJgEQP9niDJjLhcNBtmloOvHk,741
7
+ treenode/signals.py,sha256=ERrlKjGqhYaPYVKKRk1JBBlPFOmJKpJ6bXsJavcTlo0,518
8
+ treenode/tests.py,sha256=2uDafv3Ns6f7Vy1ekUtgYxCZEi1KRyesZDTAFhYcX-E,63
9
+ treenode/urls.py,sha256=krHvVigc_dxC0z5hEd2rgeH6th8jW7qJY3Qbia-419Y,240
10
+ treenode/version.py,sha256=7TedO4TEmiuyAVChT2JQ953v0toWdhTtfA4d5VIsuCE,220
11
+ treenode/widgets.py,sha256=3kSby7v-gpyUHmAIFZCENM8hzT1xcHJFB4op8XU6YEQ,4035
12
+ treenode/admin/__init__.py,sha256=XNEYHdF5lKb0vpdlVxdR2fxj5oUgzyx1YyCwsv0gxHw,100
13
+ treenode/admin/admin.py,sha256=cbiqlzXwK-W73DTJqvXo_abtnK1JwEwyBEOl0bmgsa4,7507
14
+ treenode/admin/changelist.py,sha256=KUYS9MaR8Ck_1xmMqupobxWKarrJEqmHuEG32CL01Bo,1662
15
+ treenode/admin/exporter.py,sha256=QE74V6W3tvwA5kCvBt1MmVlLOaWh-o8EU63cgmiwD5Q,5724
16
+ treenode/admin/importer.py,sha256=hK3D-1DZcoowGblRluGzng3n5Bf__hMsbNaIGXRpRdg,6263
17
+ treenode/admin/mixin.py,sha256=7hcjoh8W2_R2in0EorNjVTXakYP3I1mMZKIr65UsU-g,10677
18
+ treenode/managers/__init__.py,sha256=c7F9Ku9489Hv6lTpUY2nbyBlWFCXBWAkNBm4xTKcjL8,186
19
+ treenode/managers/managers.py,sha256=G1dayrqaEX5nQiKsHS_2y6o3iXKIAn66RKArHttN-kU,7174
20
+ treenode/managers/queries.py,sha256=Kepax8SDn7G5tOlPRWBCp5Oyp49O5iMITCMBoNCm_Ak,10655
21
+ treenode/managers/tasks.py,sha256=nVlGDxNnFlZS1-oU8UH_yTXJERcGrAZ9kCmKwKO5WuY,7138
22
+ treenode/models/__init__.py,sha256=iR4ksCKoayvkIWWgGk6OUGHZC3D0mzAtgdBcS2vQPBw,188
23
+ treenode/models/decorators.py,sha256=N2dcnWqSCiEXDcYCf0zVijrbGUC8kYlqOLi_GKFmECU,1457
24
+ treenode/models/factory.py,sha256=sPUSrvo1za-r6ny3B8ptwevyjO8-iUpPNrT0eSD2kvI,1786
25
+ treenode/models/models.py,sha256=93AxBPQseV3TaKhYoX173HMgV1wIFDjwGg9YVBvtPNQ,12335
26
+ treenode/models/mixins/__init__.py,sha256=aALVKMGAWbgMAeKWS6s-NF3L5FmRX96mQxtpthOX-Ec,805
27
+ treenode/models/mixins/ancestors.py,sha256=9g-0nPHoiF_SX2kN4uDLdbWyw-TDCz1YqxLJngwTZOQ,1971
28
+ treenode/models/mixins/children.py,sha256=H9iMqgucSmwLX-3O3QUj1a2PUQTmmWZ4GPPjRZ9a5E4,2399
29
+ treenode/models/mixins/descendants.py,sha256=RKowr29JUUO3E_UDvbLbMLhbn5IUkY2eh3AXfU8XSE8,2165
30
+ treenode/models/mixins/family.py,sha256=MB5kWRVvxU_xmSgCekveTP5Vhj4wJki8bU7hzn9RNLE,1673
31
+ treenode/models/mixins/logical.py,sha256=gh5wv5XZDs5GWarU6g9zKXWNwji7SE3zSVNIpywDWjw,2190
32
+ treenode/models/mixins/node.py,sha256=D3lwWsN2eRJeS99O7CaVAhabTYWA_NSY8z60zAhFAs0,8576
33
+ treenode/models/mixins/properties.py,sha256=1O6p2tfvOesBooZeOeuXi8yfEO_o-5gn_agtKqMxU-s,3945
34
+ treenode/models/mixins/roots.py,sha256=2lDjUH22NZFUMVbVZgsdtEqDoUyHhGTiaoeo9h3KRfk,4102
35
+ treenode/models/mixins/siblings.py,sha256=4XvQS7WkgolzEZdnURhIClo-VUcpuqQ-Sc7PDYKGmFw,3069
36
+ treenode/models/mixins/tree.py,sha256=5Cew5c-NtTmz15S_ueDZ-fojHtQKZEKNxmp3W6qlC-8,14512
37
+ treenode/models/mixins/update.py,sha256=oCZkMnfT23n2n3_mnokDrtTLS_jO_lJRwG3ENkH_DE4,4948
38
+ treenode/static/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
+ treenode/static/treenode/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
+ treenode/static/treenode/css/.gitkeep,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
41
+ treenode/static/treenode/css/tree_widget.css,sha256=YVIHuZT_E5ck5N7wvYvuwgV_m-4Ta_uGFiNUoTw-zbk,4861
42
+ treenode/static/treenode/css/treenode_admin.css,sha256=w7yyyf4aFsJKlr_YJPVq6ap5ZvA5Co7ZCYq6ipuV958,3291
43
+ treenode/static/treenode/css/treenode_tabs.css,sha256=pbulaUEhNMQFNWXFQrRN1zMyKjoSqLAnjNygpouPUfE,879
44
+ treenode/static/treenode/js/.gitkeep,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
45
+ treenode/static/treenode/js/lz-string.min.js,sha256=TAnTJQd2AlLqT9M2TU7GFjnoj9SIfwLeZnpEtLkP624,4718
46
+ treenode/static/treenode/js/tree_widget.js,sha256=2LU8IIJD5TXbWVGjCEgrj5aYnUYtwBZECzj25XLtxNc,9633
47
+ treenode/static/treenode/js/treenode_admin.js,sha256=IU3Zj2Q-WBxftl0zsjeRPrt1xOzzdEKlzGEMThi3hrk,9706
48
+ treenode/static/treenode/vendors/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
+ treenode/static/treenode/vendors/jquery-ui/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
50
+ treenode/static/treenode/vendors/jquery-ui/AUTHORS.txt,sha256=geZ7JfVsTJq8Wgjrcm1p56Zqjxx7PL9FkxhKiN8dQg8,14848
51
+ treenode/static/treenode/vendors/jquery-ui/LICENSE.txt,sha256=9kkCXPZcjc2FiLCTeIO6elK9ttNRKUZKNqFgLrACUPE,1818
52
+ treenode/static/treenode/vendors/jquery-ui/index.html,sha256=RSv8cA662qJdc02OX-UmpaBJoZrngoQqnNWVZNn9GQQ,24229
53
+ treenode/static/treenode/vendors/jquery-ui/jquery-ui.css,sha256=O8mmheetXPeKUa-iDk8fdxDhs096eWIi10qLJb6OXyw,17171
54
+ treenode/static/treenode/vendors/jquery-ui/jquery-ui.js,sha256=XtnlPiKuVzMh-cef76mBCNCTPqk2cIlKBgmTHfRBDog,146430
55
+ treenode/static/treenode/vendors/jquery-ui/jquery-ui.min.css,sha256=Yv2MureLOGZicbgEv28X9ft9o0SRBZ84PWIgg6h_V2E,15003
56
+ treenode/static/treenode/vendors/jquery-ui/jquery-ui.min.js,sha256=FT6F7DDm5J1Qz6B5fv3e8jnmx9QyyfR2qUeTptb73bA,70364
57
+ treenode/static/treenode/vendors/jquery-ui/jquery-ui.structure.css,sha256=392jP1trXFqS1OVv2X50px0F8w1DeTyIvvHL9wxNXi0,328
58
+ treenode/static/treenode/vendors/jquery-ui/jquery-ui.structure.min.css,sha256=cXLe7UDVCN5SvPpbb3HJdndQIk1hlWMrzyRRYO9rcLs,208
59
+ treenode/static/treenode/vendors/jquery-ui/jquery-ui.theme.css,sha256=grsB4hVvwiBxnNsG1c_ocH1YK76CExOav_dn_BsxepU,17140
60
+ treenode/static/treenode/vendors/jquery-ui/jquery-ui.theme.min.css,sha256=oW6e3DmGiKSNLzo3HCUnjes9ykGZ6gXEtl3LjiQIkMs,13708
61
+ treenode/static/treenode/vendors/jquery-ui/package.json,sha256=AvCATruWzbwY7NsfzcUR2WqPjslGO8M7hHzTj1AYDQY,2122
62
+ treenode/static/treenode/vendors/jquery-ui/external/jquery/jquery.js,sha256=eKhayi8LEQwp4NKxN-CfCh-3qOVUtJn3QNZ0TciWLP4,285314
63
+ treenode/static/treenode/vendors/jquery-ui/images/ui-icons_444444_256x240.png,sha256=YW09ms2dyfoVEOT_NEpaWXMMRLJxVIqu8hku63SoCEA,7107
64
+ treenode/static/treenode/vendors/jquery-ui/images/ui-icons_555555_256x240.png,sha256=HP6KoU5aOllbVtaudhmNOLhBIzi_0oCuxBjwyhDATOo,7105
65
+ treenode/static/treenode/vendors/jquery-ui/images/ui-icons_777620_256x240.png,sha256=NxKGIMNmb6YYZor0RuijQ7PTK5BoA9b5qXLjYTMFvXo,4615
66
+ treenode/static/treenode/vendors/jquery-ui/images/ui-icons_777777_256x240.png,sha256=TLerPghEHzRXKPUuwr0VPT1PusnYFUe1-k1QIyeXe7g,7115
67
+ treenode/static/treenode/vendors/jquery-ui/images/ui-icons_cc0000_256x240.png,sha256=S36ETnNcCxoWxmxAkVl0P3LYEDInjxi9GnG_g5uho1c,4615
68
+ treenode/static/treenode/vendors/jquery-ui/images/ui-icons_ffffff_256x240.png,sha256=SBgnaQEkoyko7AbSFy0lDoDzAup6bkVQ74XyenFleUM,6395
69
+ treenode/templates/.gitkeep,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
70
+ treenode/templates/treenode/.gitkeep,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
71
+ treenode/templates/treenode/admin/.gitkeep,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
72
+ treenode/templates/treenode/admin/treenode_ajax_rows.html,sha256=zFyPaTbSyxRjOqQ85SMv__qTIYDjEna6chYODBypDZA,224
73
+ treenode/templates/treenode/admin/treenode_changelist.html,sha256=JiUwX33w5WwegX7gM2ctw-bx9XOyXwKt9QB_6g7Dfz8,2283
74
+ treenode/templates/treenode/admin/treenode_import_export.html,sha256=bgmue5y8SwzgbFQls8DjslkX1EZmrp5enUFHqTmhUfI,2977
75
+ treenode/templates/treenode/admin/treenode_rows.html,sha256=de90wKpNdVB2C5CGnIkStBB0dWCDkmWr_pluV_NgBUs,1857
76
+ treenode/templates/treenode/widgets/tree_widget.html,sha256=GKcCU-B2FkkJ2BSOuXOw9e_PdYTtADcvyITEXqOlZ9Y,723
77
+ treenode/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
78
+ treenode/templatetags/treenode_admin.py,sha256=5f5oqAS4zC_f0kkJRsm5MqXHjsKJXn8GslbZcbMivxg,2736
79
+ treenode/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
80
+ treenode/utils/jwt_auth.py,sha256=LSTMuBFbH1DPJ0pVUnD_T1owb6GurpkJcCxpsXDL4HQ,845
81
+ treenode/utils/db/__init__.py,sha256=RwicAcJSI1nhIPWLdT7j9TFsgOc9834VDn9lVn54GlY,247
82
+ treenode/utils/db/compiler.py,sha256=PgD9ybS5H8OUHw1gkFBQHhnrf5HiCx8QXUMRhydwh7o,3824
83
+ treenode/utils/db/db_vendor.py,sha256=4SyEHl51jVCDB3is4omHCf2bTB_QV3RvemUQYxJP5m0,930
84
+ treenode/utils/db/service.py,sha256=PF85Yhz2xUWFFCzpLYotmiNTZXXEH61rhswslSxEUds,2640
85
+ treenode/utils/db/sqlcompat.py,sha256=K71ggkKIvpdTtHQ6Y4qcbo6cj2eYiEfy6DlVBr8Po1E,4460
86
+ treenode/utils/db/sqlquery.py,sha256=KXcfKbbaBF-D134H_2DiPQtjedR79SJNXPJc0msZYEc,1938
87
+ treenode/views/__init__.py,sha256=ppxbBx51TUaKstJFpAd_DTmbKjbZGmVMLNYSpgUKnd0,111
88
+ treenode/views/autoapi.py,sha256=X7r8hgzJ9LGO1k_keh9YfLk3dzVbjW-sVte5Ddp_HVE,3771
89
+ treenode/views/autocomplete.py,sha256=ERGJT4jfCE2GUv94Ja8SgJyG1FOAyqIeB0PKH1fyU7g,1417
90
+ treenode/views/children.py,sha256=seO0SNKriRY2FJOO1oZgX42iKolXMf3EF76GrfdJZLQ,1111
91
+ treenode/views/common.py,sha256=kUN3IgMZCdNdJ2haxB9MTGcn2rctyFUAHNagzcu9wXk,594
92
+ treenode/views/crud.py,sha256=RI5rdyD4hZTszjZFThByxi_lkAeJlqbDCXFkD8iyzKE,7424
93
+ treenode/views/search.py,sha256=c_GyooT3jyoNa96bBxfoWruRN1wIw-ZGYvwGKkGojTs,1501
94
+ django_fast_treenode-3.2.0.dist-info/METADATA,sha256=q3lF9MZCoD6ZJO9DuApkCEwIEMqPCr75zjvUWOpZakw,10377
95
+ django_fast_treenode-3.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
96
+ django_fast_treenode-3.2.0.dist-info/top_level.txt,sha256=fmgxHbXyx1O2MPi_9kjx8aL9L-8TmV0gre4Go8XgqFk,9
97
+ django_fast_treenode-3.2.0.dist-info/RECORD,,
treenode/admin/admin.py CHANGED
@@ -69,13 +69,13 @@ class TreeNodeModelAdmin(AdminMixin, admin.ModelAdmin):
69
69
  """Meta Class."""
70
70
 
71
71
  css = {"all": (
72
- "css/treenode_admin.css",
73
- "vendors/jquery-ui/jquery-ui.css",
72
+ "treenode/css/treenode_admin.css",
73
+ "treenode/vendors/jquery-ui/jquery-ui.css",
74
74
  )}
75
75
  js = (
76
- "vendors/jquery-ui/jquery-ui.js",
77
- # "js/lz-string.min.js",
78
- "js/treenode_admin.js",
76
+ "treenode/vendors/jquery-ui/jquery-ui.js",
77
+ # "treenode/js/lz-string.min.js",
78
+ "treenode/js/treenode_admin.js",
79
79
  )
80
80
 
81
81
  def __init__(self, model, admin_site):
treenode/admin/mixin.py CHANGED
@@ -62,7 +62,7 @@ class AdminMixin(admin.ModelAdmin):
62
62
  return custom_urls + default_urls
63
63
 
64
64
  def render_changelist_rows(self, objs: list, request):
65
- """Rander rows for incert to changelist."""
65
+ """Render rows for insert into changelist."""
66
66
  list_display = list(self.get_list_display(request))
67
67
  checkbox_field_name = ACTION_CHECKBOX_NAME
68
68
  if checkbox_field_name not in list_display:
@@ -227,7 +227,7 @@ class AdminMixin(admin.ModelAdmin):
227
227
 
228
228
  def import_view(self, request):
229
229
  """
230
- Impoern View.
230
+ Import View.
231
231
 
232
232
  Handles file upload and initiates import processing via
233
233
  TreeNodeImporter.
@@ -242,14 +242,14 @@ class AdminMixin(admin.ModelAdmin):
242
242
  extension = os.path.splitext(filename)[1].lower().lstrip('.')
243
243
  if extension not in ['csv', 'tsv', 'json', 'xlsx', 'yaml']:
244
244
  return JsonResponse(
245
- {"error": _(f"Invalid file format ({extension}.")},
245
+ {"error": _(f"Invalid file format ({extension}).")},
246
246
  status=200
247
247
  )
248
248
  importer = self.TreeNodeImporter(self.model, file, extension)
249
249
  importer.parse()
250
250
  result = importer.import_tree()
251
251
 
252
- return render(request, "admin/treenode_import_export.html", {
252
+ return render(request, "treenode/admin/treenode_import_export.html", {
253
253
  "created_count": result.get("created", 0),
254
254
  "updated_count": result.get("updated", 0),
255
255
  "errors": result.get("errors", []),
@@ -258,7 +258,7 @@ class AdminMixin(admin.ModelAdmin):
258
258
 
259
259
  return render(
260
260
  request,
261
- "admin/treenode_import_export.html",
261
+ "treenode/admin/treenode_import_export.html",
262
262
  {"import_active": True}
263
263
  )
264
264
 
@@ -284,7 +284,7 @@ class AdminMixin(admin.ModelAdmin):
284
284
 
285
285
  return render(
286
286
  request,
287
- "admin/treenode_import_export.html",
287
+ "treenode/admin/treenode_import_export.html",
288
288
  {"import_active": False}
289
289
  )
290
290
 
@@ -207,7 +207,7 @@ class TreeNodeManager(models.Manager):
207
207
 
208
208
  WARNING: Unsafe low-level update bypassing all TreeNode protections.
209
209
  Use only when bypassing _path/_depth/priority safety checks is
210
- inte
210
+ intentional.
211
211
  """
212
212
  return models.QuerySet(self.model, using=self.db)\
213
213
  .bulk_update(*args, **kwargs)
@@ -40,18 +40,18 @@ class TreeNodeNodeMixin(models.Model):
40
40
  self.refresh()
41
41
  return self._depth
42
42
 
43
- def distance_to(self, targer):
43
+ def distance_to(self, target):
44
44
  """Return number of edges on shortest path between two nodes."""
45
45
  self_path = self.query(objects='ancestors')
46
- targer_path = targer.query(objects='ancestors')
46
+ target_path = target.query(objects='ancestors')
47
47
 
48
48
  i = 0
49
- for a, b in zip(self_path, targer_path):
49
+ for a, b in zip(self_path, target_path):
50
50
  if a != b:
51
51
  break
52
52
  i += 1
53
53
 
54
- return (len(self_path) - i) + (len(targer_path) - i)
54
+ return (len(self_path) - i) + (len(target_path) - i)
55
55
 
56
56
  def get_index(self):
57
57
  """Get the node index (self, index in node.parent.children list)."""
treenode/models/models.py CHANGED
@@ -14,12 +14,12 @@ Features:
14
14
  - Provides a caching mechanism to optimize performance.
15
15
  - Includes methods for tree traversal, manipulation, and serialization.
16
16
 
17
+ With full support for SQL queues, deferred execution,
18
+ custom sorting, and a sleek architecture without unnecessary duplication.
19
+
17
20
  Version: 3.0.7
18
21
  Author: Timur Kady
19
22
  Email: timurkady@yandex.com
20
-
21
- Причём с абсолютной поддержкой SQL-очередей, deferred execution,
22
- кастомной сортировки и крутой архитектурой без лишнего дублирования.
23
23
  """
24
24
 
25
25
  from __future__ import annotations
@@ -38,6 +38,8 @@ from ..cache import treenode_cache as cache
38
38
  from ..settings import SEGMENT_LENGTH, BASE
39
39
  from ..signals import disable_signals
40
40
 
41
+ import logging
42
+ logger = logging.getLogger(__name__)
41
43
 
42
44
  class TreeNodeModel(
43
45
  mx.TreeNodeAncestorsMixin, mx.TreeNodeChildrenMixin,
@@ -156,10 +158,10 @@ class TreeNodeModel(
156
158
  # Update subtree
157
159
  self._update_path(self.parent_id)
158
160
  self.sqlq.flush()
159
- # Clead cache
161
+ # Clear cache
160
162
  self.clear_cache()
161
163
 
162
- # Saving and Udating methods ----------------------------------
164
+ # Saving and Updating methods ----------------------------------
163
165
 
164
166
  def save(self, *args, **kwargs):
165
167
  """
@@ -193,7 +195,10 @@ class TreeNodeModel(
193
195
  if is_move:
194
196
  self._meta.model.tasks.add("update", state["parent_id"])
195
197
  else:
196
- print("TreeNodeModel error: oject not found in DB! WTF, MF!")
198
+ logger.error(
199
+ "TreeNodeModel save error: object with pk %s not found in DB",
200
+ self.pk,
201
+ )
197
202
  else:
198
203
  is_new = True
199
204
 
@@ -335,11 +340,11 @@ class TreeNodeModel(
335
340
  queue.extend(model.objects.filter(parent=node))
336
341
 
337
342
  if verbose and errors:
338
- print("Tree integrity check failed:")
343
+ logger.error("Tree integrity check failed:")
339
344
  for err in errors:
340
- print(" -", err)
345
+ logger.error(" - %s", err)
341
346
  elif verbose:
342
- print("Tree integrity: OK ✅")
347
+ logger.info("Tree integrity: OK ✅")
343
348
 
344
349
  return errors
345
350
 
treenode/settings.py CHANGED
@@ -18,11 +18,14 @@ SEGMENT_LENGTH = getattr(settings, "TREENODE_SEGMENT_LENGTH", 3)
18
18
  # Serialization dictionary: hexadecimal encoding, fixed segment size
19
19
  SEGMENT_BASE = 16
20
20
 
21
- # Nubber children per one tree node
21
+ # Number of children per tree node
22
22
  BASE = SEGMENT_BASE ** SEGMENT_LENGTH # 4096
23
23
 
24
24
 
25
25
  TREENODE_PAD_CHAR = getattr(settings, "TREENODE_PAD_CHAR", "'0'")
26
26
 
27
+ # Optional JWT protection for API endpoints
28
+ API_USE_JWT = getattr(settings, "TREENODE_API_USE_JWT", False)
29
+
27
30
 
28
31
  # The End
File without changes
File without changes
File without changes
@@ -3,8 +3,9 @@
3
3
 
4
4
  {% block extrastyle %}
5
5
  {{ block.super }}
6
- <link rel="stylesheet" href="{% static 'admin/css/forms.css' %}">
7
- <link rel="stylesheet" href="{% static 'css/treenode_tabs.css' %}">
6
+ {# Load built-in admin form styles #}
7
+ <link rel="stylesheet" href="{% static '/admin/css/forms.css' %}">
8
+ <link rel="stylesheet" href="{% static 'treenode/css/treenode_tabs.css' %}">
8
9
  {% endblock %}
9
10
 
10
11
  {% block content %}
@@ -0,0 +1,25 @@
1
+ import jwt
2
+ from functools import wraps
3
+
4
+ from django.conf import settings
5
+ from django.http import JsonResponse
6
+
7
+
8
+ def jwt_required(view_func):
9
+ """Ensure that request has valid JWT token."""
10
+
11
+ @wraps(view_func)
12
+ def _wrapped(request, *args, **kwargs):
13
+ auth_header = request.META.get("HTTP_AUTHORIZATION", "")
14
+ if not auth_header.startswith("Bearer "):
15
+ return JsonResponse({"detail": "Authorization header missing"}, status=401)
16
+ token = auth_header.split(" ", 1)[1]
17
+ try:
18
+ payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
19
+ except jwt.PyJWTError:
20
+ return JsonResponse({"detail": "Invalid token"}, status=401)
21
+ request.jwt_payload = payload
22
+ return view_func(request, *args, **kwargs)
23
+
24
+ return _wrapped
25
+
treenode/version.py CHANGED
@@ -4,9 +4,9 @@ TreeNode Version Module
4
4
 
5
5
  This module defines the current version of the TreeNode package.
6
6
 
7
- Version: 3.0.8
7
+ Version: 3.2.0
8
8
  Author: Timur Kady
9
9
  Email: timurkady@yandex.com
10
10
  """
11
11
 
12
- __version__ = '3.0.8'
12
+ __version__ = '3.2.0'
treenode/views/autoapi.py CHANGED
@@ -1,91 +1,95 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- Route generator for all models inherited from TreeNodeModel
4
-
5
- Version: 3.0.0
6
- Author: Timur Kady
7
- Email: timurkady@yandex.com
8
- """
9
-
10
-
11
- from django.apps import apps
12
- from django.urls import path
13
- from django.conf import settings
14
- from django.contrib.auth.decorators import login_required
15
-
16
- from ..models import TreeNodeModel
17
- from .autocomplete import TreeNodeAutocompleteView
18
- from .children import TreeChildrenView
19
- from .search import TreeSearchView
20
- from .crud import TreeNodeBaseAPIView
21
-
22
-
23
- class AutoTreeAPI:
24
- """Auto-discover and expose TreeNode-based APIs."""
25
-
26
- def __init__(self, base_view=TreeNodeBaseAPIView, base_url="api"):
27
- """Init auto-discover."""
28
- self.base_view = base_view
29
- self.base_url = base_url
30
-
31
- def protect_view(self, view, model):
32
- """
33
- Protect view.
34
-
35
- Protects view with login_required if needed, based on model attribute
36
- or global settings.
37
- """
38
- if getattr(model, 'api_login_required', None) is True:
39
- return login_required(view)
40
- if getattr(settings, 'TREENODE_API_LOGIN_REQUIRED', False):
41
- return login_required(view)
42
- return view
43
-
44
- def discover(self):
45
- """Scan models and generate API urls."""
46
- urls = [
47
- # Admin and Widget end-points
48
- path("widget/autocomplete/", TreeNodeAutocompleteView.as_view(), name="tree_autocomplete"), # noqa: D501
49
- path("widget/children/", TreeChildrenView.as_view(), name="tree_children"), # noqa: D501
50
- path("widget/search/", TreeSearchView.as_view(), name="tree_search"), # noqa: D501
51
- ]
52
- for model in apps.get_models():
53
- if issubclass(model, TreeNodeModel) and model is not TreeNodeModel:
54
- model_name = model._meta.model_name
55
-
56
- # Dynamically create an API view class for the model
57
- api_view_class = type(
58
- f"{model_name.capitalize()}APIView",
59
- (self.base_view,),
60
- {"model": model}
61
- )
62
-
63
- # List of API actions and their corresponding URL patterns
64
- action_patterns = [
65
- # List / Create
66
- ("", None, f"{model_name}-list"),
67
- # Retrieve / Update / Delete
68
- ("<int:pk>/", None, f"{model_name}-detail"),
69
- ("tree/", {'action': 'tree'}, f"{model_name}-tree"),
70
- # Direct children
71
- ("<int:pk>/children/", {'action': 'children'}, f"{model_name}-children"), # noqa: D501
72
- # All descendants
73
- ("<int:pk>/descendants/", {'action': 'descendants'}, f"{model_name}-descendants"), # noqa: D501
74
- # Ancestors + Self + Descendants
75
- ("<int:pk>/family/", {'action': 'family'}, f"{model_name}-family"), # noqa: D501
76
- ]
77
-
78
- # Create secured view instance once
79
- view = self.protect_view(api_view_class.as_view(), model)
80
-
81
- # Automatically build all paths for this model
82
- for url_suffix, extra_kwargs, route_name in action_patterns:
83
- urls.append(
84
- path(
85
- f"{self.base_url}/{model_name}/{url_suffix}",
86
- view,
87
- extra_kwargs or {},
88
- name=route_name
89
- )
90
- )
91
- return urls
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Route generator for all models inherited from TreeNodeModel
4
+
5
+ Version: 3.0.0
6
+ Author: Timur Kady
7
+ Email: timurkady@yandex.com
8
+ """
9
+
10
+
11
+ from django.apps import apps
12
+ from django.urls import path
13
+ from django.conf import settings
14
+ from django.contrib.auth.decorators import login_required
15
+
16
+ from ..models import TreeNodeModel
17
+ from ..settings import API_USE_JWT
18
+ from ..utils.jwt_auth import jwt_required
19
+ from .autocomplete import TreeNodeAutocompleteView
20
+ from .children import TreeChildrenView
21
+ from .crud import TreeNodeBaseAPIView
22
+ from .search import TreeSearchView
23
+
24
+
25
+ class AutoTreeAPI:
26
+ """Auto-discover and expose TreeNode-based APIs."""
27
+
28
+ def __init__(self, base_view=TreeNodeBaseAPIView, base_url="api"):
29
+ """Init auto-discover."""
30
+ self.base_view = base_view
31
+ self.base_url = base_url
32
+
33
+ def protect_view(self, view, model):
34
+ """
35
+ Protect view.
36
+
37
+ Protects view with login_required if needed, based on model attribute
38
+ or global settings.
39
+ """
40
+ if API_USE_JWT:
41
+ return jwt_required(view)
42
+ if getattr(model, 'api_login_required', None) is True:
43
+ return login_required(view)
44
+ if getattr(settings, 'TREENODE_API_LOGIN_REQUIRED', False):
45
+ return login_required(view)
46
+ return view
47
+
48
+ def discover(self):
49
+ """Scan models and generate API urls."""
50
+ urls = [
51
+ # Admin and Widget end-points
52
+ path("widget/autocomplete/", TreeNodeAutocompleteView.as_view(), name="tree_autocomplete"), # noqa: D501
53
+ path("widget/children/", TreeChildrenView.as_view(), name="tree_children"), # noqa: D501
54
+ path("widget/search/", TreeSearchView.as_view(), name="tree_search"), # noqa: D501
55
+ ]
56
+ for model in apps.get_models():
57
+ if issubclass(model, TreeNodeModel) and model is not TreeNodeModel:
58
+ model_name = model._meta.model_name
59
+
60
+ # Dynamically create an API view class for the model
61
+ api_view_class = type(
62
+ f"{model_name.capitalize()}APIView",
63
+ (self.base_view,),
64
+ {"model": model}
65
+ )
66
+
67
+ # List of API actions and their corresponding URL patterns
68
+ action_patterns = [
69
+ # List / Create
70
+ ("", None, f"{model_name}-list"),
71
+ # Retrieve / Update / Delete
72
+ ("<int:pk>/", None, f"{model_name}-detail"),
73
+ ("tree/", {'action': 'tree'}, f"{model_name}-tree"),
74
+ # Direct children
75
+ ("<int:pk>/children/", {'action': 'children'}, f"{model_name}-children"), # noqa: D501
76
+ # All descendants
77
+ ("<int:pk>/descendants/", {'action': 'descendants'}, f"{model_name}-descendants"), # noqa: D501
78
+ # Ancestors + Self + Descendants
79
+ ("<int:pk>/family/", {'action': 'family'}, f"{model_name}-family"), # noqa: D501
80
+ ]
81
+
82
+ # Create secured view instance once
83
+ view = self.protect_view(api_view_class.as_view(), model)
84
+
85
+ # Automatically build all paths for this model
86
+ for url_suffix, extra_kwargs, route_name in action_patterns:
87
+ urls.append(
88
+ path(
89
+ f"{self.base_url}/{model_name}/{url_suffix}",
90
+ view,
91
+ extra_kwargs or {},
92
+ name=route_name
93
+ )
94
+ )
95
+ return urls
treenode/widgets.py CHANGED
@@ -24,8 +24,8 @@ class TreeWidget(forms.Widget):
24
24
  class Media:
25
25
  """Meta class to define required CSS and JS files."""
26
26
 
27
- css = {"all": ("css/tree_widget.css",)}
28
- js = ("js/tree_widget.js",)
27
+ css = {"all": ("treenode/css/tree_widget.css",)}
28
+ js = ("treenode/js/tree_widget.js",)
29
29
 
30
30
  def build_attrs(self, base_attrs, extra_attrs=None):
31
31
  """Build attributes for the widget."""
@@ -1,93 +0,0 @@
1
- django_fast_treenode-3.0.8.dist-info/licenses/LICENSE,sha256=SSYqS84FCnAW7tAxmjBKU8qAa8Jv4VGPuSSGeHwWtJE,1095
2
- treenode/__init__.py,sha256=3z1hWpHyy4wg6uz7HCmRi9FaXYeN5CfANVpa77UIoPw,53
3
- treenode/apps.py,sha256=QlwjNDM9rkUoWB8Vm8-OkS6lNx0-aTByuGZlu9wrQMs,1832
4
- treenode/cache.py,sha256=2jUiiecfFxwB7QFukpU4u0FnDzGH6hNRfo6KAYvs6vM,8447
5
- treenode/forms.py,sha256=V-upmbYSW1BbuXdSBGExHxw_j5TTUheaHvreK9tSGTE,3155
6
- treenode/settings.py,sha256=oSkcKXNVd28HXrlWZIH2VYinCMq-UdCDlX4KD0Qc_Xk,631
7
- treenode/signals.py,sha256=ERrlKjGqhYaPYVKKRk1JBBlPFOmJKpJ6bXsJavcTlo0,518
8
- treenode/tests.py,sha256=2uDafv3Ns6f7Vy1ekUtgYxCZEi1KRyesZDTAFhYcX-E,63
9
- treenode/urls.py,sha256=krHvVigc_dxC0z5hEd2rgeH6th8jW7qJY3Qbia-419Y,240
10
- treenode/version.py,sha256=5TH3IchoS9D8YEFnTIlRrjoALxMdemh1PCN0Bb9Jnss,220
11
- treenode/widgets.py,sha256=VItPvN9XgaSRI_MZjKEmtaHDJcn2bDIQIppwKjXmYQM,4017
12
- treenode/admin/__init__.py,sha256=XNEYHdF5lKb0vpdlVxdR2fxj5oUgzyx1YyCwsv0gxHw,100
13
- treenode/admin/admin.py,sha256=uORuVSx-p0MYXLRVl54cBa7IAL9zicyeYTX3Cv5pBDc,7462
14
- treenode/admin/changelist.py,sha256=KUYS9MaR8Ck_1xmMqupobxWKarrJEqmHuEG32CL01Bo,1662
15
- treenode/admin/exporter.py,sha256=QE74V6W3tvwA5kCvBt1MmVlLOaWh-o8EU63cgmiwD5Q,5724
16
- treenode/admin/importer.py,sha256=hK3D-1DZcoowGblRluGzng3n5Bf__hMsbNaIGXRpRdg,6263
17
- treenode/admin/mixin.py,sha256=jOkKKt0CpknGokluW8T1YB7VhkCkaAAMYBuptbYDXAw,10648
18
- treenode/managers/__init__.py,sha256=c7F9Ku9489Hv6lTpUY2nbyBlWFCXBWAkNBm4xTKcjL8,186
19
- treenode/managers/managers.py,sha256=8OaFxtajyR1d7-UHyiUbifMBEF9cjfHTIEYPkYUWmt0,7166
20
- treenode/managers/queries.py,sha256=Kepax8SDn7G5tOlPRWBCp5Oyp49O5iMITCMBoNCm_Ak,10655
21
- treenode/managers/tasks.py,sha256=nVlGDxNnFlZS1-oU8UH_yTXJERcGrAZ9kCmKwKO5WuY,7138
22
- treenode/models/__init__.py,sha256=iR4ksCKoayvkIWWgGk6OUGHZC3D0mzAtgdBcS2vQPBw,188
23
- treenode/models/decorators.py,sha256=N2dcnWqSCiEXDcYCf0zVijrbGUC8kYlqOLi_GKFmECU,1457
24
- treenode/models/factory.py,sha256=sPUSrvo1za-r6ny3B8ptwevyjO8-iUpPNrT0eSD2kvI,1786
25
- treenode/models/models.py,sha256=n_2nCJzBOEcmh3r-GCoHV3ZiTX8Xe1ewK9SPptvI_M0,12273
26
- treenode/models/mixins/__init__.py,sha256=aALVKMGAWbgMAeKWS6s-NF3L5FmRX96mQxtpthOX-Ec,805
27
- treenode/models/mixins/ancestors.py,sha256=9g-0nPHoiF_SX2kN4uDLdbWyw-TDCz1YqxLJngwTZOQ,1971
28
- treenode/models/mixins/children.py,sha256=H9iMqgucSmwLX-3O3QUj1a2PUQTmmWZ4GPPjRZ9a5E4,2399
29
- treenode/models/mixins/descendants.py,sha256=RKowr29JUUO3E_UDvbLbMLhbn5IUkY2eh3AXfU8XSE8,2165
30
- treenode/models/mixins/family.py,sha256=MB5kWRVvxU_xmSgCekveTP5Vhj4wJki8bU7hzn9RNLE,1673
31
- treenode/models/mixins/logical.py,sha256=gh5wv5XZDs5GWarU6g9zKXWNwji7SE3zSVNIpywDWjw,2190
32
- treenode/models/mixins/node.py,sha256=8kqYdFTyZK5WsSxguuX3jBo1nZehDmwIBjm38S6QJw4,8576
33
- treenode/models/mixins/properties.py,sha256=1O6p2tfvOesBooZeOeuXi8yfEO_o-5gn_agtKqMxU-s,3945
34
- treenode/models/mixins/roots.py,sha256=2lDjUH22NZFUMVbVZgsdtEqDoUyHhGTiaoeo9h3KRfk,4102
35
- treenode/models/mixins/siblings.py,sha256=4XvQS7WkgolzEZdnURhIClo-VUcpuqQ-Sc7PDYKGmFw,3069
36
- treenode/models/mixins/tree.py,sha256=5Cew5c-NtTmz15S_ueDZ-fojHtQKZEKNxmp3W6qlC-8,14512
37
- treenode/models/mixins/update.py,sha256=oCZkMnfT23n2n3_mnokDrtTLS_jO_lJRwG3ENkH_DE4,4948
38
- treenode/static/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
- treenode/static/css/.gitkeep,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
40
- treenode/static/css/tree_widget.css,sha256=YVIHuZT_E5ck5N7wvYvuwgV_m-4Ta_uGFiNUoTw-zbk,4861
41
- treenode/static/css/treenode_admin.css,sha256=w7yyyf4aFsJKlr_YJPVq6ap5ZvA5Co7ZCYq6ipuV958,3291
42
- treenode/static/css/treenode_tabs.css,sha256=pbulaUEhNMQFNWXFQrRN1zMyKjoSqLAnjNygpouPUfE,879
43
- treenode/static/js/.gitkeep,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
44
- treenode/static/js/lz-string.min.js,sha256=TAnTJQd2AlLqT9M2TU7GFjnoj9SIfwLeZnpEtLkP624,4718
45
- treenode/static/js/tree_widget.js,sha256=2LU8IIJD5TXbWVGjCEgrj5aYnUYtwBZECzj25XLtxNc,9633
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
- treenode/static/vendors/jquery-ui/images/ui-icons_444444_256x240.png,sha256=YW09ms2dyfoVEOT_NEpaWXMMRLJxVIqu8hku63SoCEA,7107
61
- treenode/static/vendors/jquery-ui/images/ui-icons_555555_256x240.png,sha256=HP6KoU5aOllbVtaudhmNOLhBIzi_0oCuxBjwyhDATOo,7105
62
- treenode/static/vendors/jquery-ui/images/ui-icons_777620_256x240.png,sha256=NxKGIMNmb6YYZor0RuijQ7PTK5BoA9b5qXLjYTMFvXo,4615
63
- treenode/static/vendors/jquery-ui/images/ui-icons_777777_256x240.png,sha256=TLerPghEHzRXKPUuwr0VPT1PusnYFUe1-k1QIyeXe7g,7115
64
- treenode/static/vendors/jquery-ui/images/ui-icons_cc0000_256x240.png,sha256=S36ETnNcCxoWxmxAkVl0P3LYEDInjxi9GnG_g5uho1c,4615
65
- treenode/static/vendors/jquery-ui/images/ui-icons_ffffff_256x240.png,sha256=SBgnaQEkoyko7AbSFy0lDoDzAup6bkVQ74XyenFleUM,6395
66
- treenode/templates/.gitkeep,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
67
- treenode/templates/treenode/.gitkeep,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
68
- treenode/templates/treenode/admin/.gitkeep,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
69
- treenode/templates/treenode/admin/treenode_ajax_rows.html,sha256=zFyPaTbSyxRjOqQ85SMv__qTIYDjEna6chYODBypDZA,224
70
- treenode/templates/treenode/admin/treenode_changelist.html,sha256=JiUwX33w5WwegX7gM2ctw-bx9XOyXwKt9QB_6g7Dfz8,2283
71
- treenode/templates/treenode/admin/treenode_import_export.html,sha256=K6L_JnhGpWgQdduXSfAJ0UuTBfSN4ylFw9w_e5tPDDE,2926
72
- treenode/templates/treenode/admin/treenode_rows.html,sha256=de90wKpNdVB2C5CGnIkStBB0dWCDkmWr_pluV_NgBUs,1857
73
- treenode/templates/treenode/widgets/tree_widget.html,sha256=GKcCU-B2FkkJ2BSOuXOw9e_PdYTtADcvyITEXqOlZ9Y,723
74
- treenode/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
- treenode/templatetags/treenode_admin.py,sha256=5f5oqAS4zC_f0kkJRsm5MqXHjsKJXn8GslbZcbMivxg,2736
76
- treenode/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
- treenode/utils/db/__init__.py,sha256=RwicAcJSI1nhIPWLdT7j9TFsgOc9834VDn9lVn54GlY,247
78
- treenode/utils/db/compiler.py,sha256=PgD9ybS5H8OUHw1gkFBQHhnrf5HiCx8QXUMRhydwh7o,3824
79
- treenode/utils/db/db_vendor.py,sha256=4SyEHl51jVCDB3is4omHCf2bTB_QV3RvemUQYxJP5m0,930
80
- treenode/utils/db/service.py,sha256=PF85Yhz2xUWFFCzpLYotmiNTZXXEH61rhswslSxEUds,2640
81
- treenode/utils/db/sqlcompat.py,sha256=K71ggkKIvpdTtHQ6Y4qcbo6cj2eYiEfy6DlVBr8Po1E,4460
82
- treenode/utils/db/sqlquery.py,sha256=KXcfKbbaBF-D134H_2DiPQtjedR79SJNXPJc0msZYEc,1938
83
- treenode/views/__init__.py,sha256=ppxbBx51TUaKstJFpAd_DTmbKjbZGmVMLNYSpgUKnd0,111
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
- treenode/views/crud.py,sha256=RI5rdyD4hZTszjZFThByxi_lkAeJlqbDCXFkD8iyzKE,7424
89
- treenode/views/search.py,sha256=c_GyooT3jyoNa96bBxfoWruRN1wIw-ZGYvwGKkGojTs,1501
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,,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes