django-fast-treenode 3.0.8__tar.gz → 3.2.1__tar.gz

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 (126) hide show
  1. {django_fast_treenode-3.0.8/django_fast_treenode.egg-info → django_fast_treenode-3.2.1}/PKG-INFO +3 -1
  2. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/README.md +1 -0
  3. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1/django_fast_treenode.egg-info}/PKG-INFO +3 -1
  4. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/django_fast_treenode.egg-info/SOURCES.txt +32 -27
  5. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/django_fast_treenode.egg-info/requires.txt +1 -0
  6. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/admin.md +1 -1
  7. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/api.md +24 -1
  8. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/apifirst.md +2 -2
  9. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/migration.md +1 -1
  10. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/roadmap.md +1 -1
  11. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/pyproject.toml +2 -1
  12. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/setup.py +3 -1
  13. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/tests/test_suite.py +11 -4
  14. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/admin/admin.py +5 -5
  15. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/admin/mixin.py +6 -6
  16. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/managers/managers.py +1 -1
  17. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/managers/tasks.py +19 -1
  18. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/mixins/__init__.py +3 -1
  19. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/mixins/children.py +1 -1
  20. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/mixins/descendants.py +1 -1
  21. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/mixins/node.py +4 -4
  22. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/mixins/roots.py +1 -1
  23. django_fast_treenode-3.2.1/treenode/models/mixins/search.py +55 -0
  24. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/mixins/siblings.py +1 -1
  25. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/models.py +31 -14
  26. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/settings.py +4 -1
  27. django_fast_treenode-3.2.1/treenode/static/treenode/vendors/jquery-ui/.gitkeep +0 -0
  28. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/templates/treenode/admin/treenode_import_export.html +3 -2
  29. django_fast_treenode-3.2.1/treenode/templatetags/__init__.py +0 -0
  30. django_fast_treenode-3.2.1/treenode/utils/__init__.py +0 -0
  31. django_fast_treenode-3.2.1/treenode/utils/db/compiler.py +72 -0
  32. django_fast_treenode-3.2.1/treenode/utils/jwt_auth.py +25 -0
  33. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/version.py +2 -2
  34. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/views/autoapi.py +95 -91
  35. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/widgets.py +2 -2
  36. django_fast_treenode-3.0.8/treenode/utils/db/compiler.py +0 -113
  37. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/LICENSE +0 -0
  38. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/MANIFEST.in +0 -0
  39. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/django_fast_treenode.egg-info/dependency_links.txt +0 -0
  40. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/django_fast_treenode.egg-info/top_level.txt +0 -0
  41. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/.gitignore +0 -0
  42. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/.nojekyll +0 -0
  43. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/about.md +0 -0
  44. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/cache.md +0 -0
  45. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/customization.md +0 -0
  46. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/dnd.md +0 -0
  47. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/import_export.md +0 -0
  48. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/index.md +0 -0
  49. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/insert-after.jpg +0 -0
  50. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/insert-as-child.jpg +0 -0
  51. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/installation.md +0 -0
  52. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/models.md +0 -0
  53. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/requirements.txt +0 -0
  54. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/using.md +0 -0
  55. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/setup.cfg +0 -0
  56. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/__init__.py +0 -0
  57. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/admin/__init__.py +0 -0
  58. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/admin/changelist.py +0 -0
  59. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/admin/exporter.py +0 -0
  60. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/admin/importer.py +0 -0
  61. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/apps.py +0 -0
  62. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/cache.py +0 -0
  63. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/forms.py +0 -0
  64. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/managers/__init__.py +0 -0
  65. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/managers/queries.py +0 -0
  66. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/__init__.py +0 -0
  67. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/decorators.py +0 -0
  68. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/factory.py +0 -0
  69. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/mixins/ancestors.py +0 -0
  70. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/mixins/family.py +0 -0
  71. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/mixins/logical.py +0 -0
  72. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/mixins/properties.py +0 -0
  73. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/mixins/tree.py +0 -0
  74. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/mixins/update.py +0 -0
  75. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/signals.py +0 -0
  76. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/static/.gitkeep +0 -0
  77. /django_fast_treenode-3.0.8/treenode/templatetags/__init__.py → /django_fast_treenode-3.2.1/treenode/static/treenode/.gitkeep +0 -0
  78. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/css/.gitkeep +0 -0
  79. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/css/tree_widget.css +0 -0
  80. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/css/treenode_admin.css +0 -0
  81. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/css/treenode_tabs.css +0 -0
  82. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/js/.gitkeep +0 -0
  83. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/js/lz-string.min.js +0 -0
  84. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/js/tree_widget.js +0 -0
  85. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/js/treenode_admin.js +0 -0
  86. /django_fast_treenode-3.0.8/treenode/utils/__init__.py → /django_fast_treenode-3.2.1/treenode/static/treenode/vendors/.gitkeep +0 -0
  87. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/vendors/jquery-ui/AUTHORS.txt +0 -0
  88. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/vendors/jquery-ui/LICENSE.txt +0 -0
  89. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/vendors/jquery-ui/external/jquery/jquery.js +0 -0
  90. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/vendors/jquery-ui/images/ui-icons_444444_256x240.png +0 -0
  91. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/vendors/jquery-ui/images/ui-icons_555555_256x240.png +0 -0
  92. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/vendors/jquery-ui/images/ui-icons_777620_256x240.png +0 -0
  93. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/vendors/jquery-ui/images/ui-icons_777777_256x240.png +0 -0
  94. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/vendors/jquery-ui/images/ui-icons_cc0000_256x240.png +0 -0
  95. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/vendors/jquery-ui/images/ui-icons_ffffff_256x240.png +0 -0
  96. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/vendors/jquery-ui/index.html +0 -0
  97. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/vendors/jquery-ui/jquery-ui.css +0 -0
  98. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/vendors/jquery-ui/jquery-ui.js +0 -0
  99. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/vendors/jquery-ui/jquery-ui.min.css +0 -0
  100. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/vendors/jquery-ui/jquery-ui.min.js +0 -0
  101. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/vendors/jquery-ui/jquery-ui.structure.css +0 -0
  102. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/vendors/jquery-ui/jquery-ui.structure.min.css +0 -0
  103. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/vendors/jquery-ui/jquery-ui.theme.css +0 -0
  104. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/vendors/jquery-ui/jquery-ui.theme.min.css +0 -0
  105. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/vendors/jquery-ui/package.json +0 -0
  106. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/templates/.gitkeep +0 -0
  107. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/templates/treenode/.gitkeep +0 -0
  108. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/templates/treenode/admin/.gitkeep +0 -0
  109. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/templates/treenode/admin/treenode_ajax_rows.html +0 -0
  110. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/templates/treenode/admin/treenode_changelist.html +0 -0
  111. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/templates/treenode/admin/treenode_rows.html +0 -0
  112. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/templates/treenode/widgets/tree_widget.html +0 -0
  113. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/templatetags/treenode_admin.py +0 -0
  114. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/tests.py +0 -0
  115. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/urls.py +0 -0
  116. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/utils/db/__init__.py +0 -0
  117. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/utils/db/db_vendor.py +0 -0
  118. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/utils/db/service.py +0 -0
  119. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/utils/db/sqlcompat.py +0 -0
  120. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/utils/db/sqlquery.py +0 -0
  121. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/views/__init__.py +0 -0
  122. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/views/autocomplete.py +0 -0
  123. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/views/children.py +0 -0
  124. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/views/common.py +0 -0
  125. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/views/crud.py +0 -0
  126. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/views/search.py +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.1
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
 
@@ -62,6 +62,7 @@ At the moment, django-fast-treeenode is, if not the best, then one of the best p
62
62
  - **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.
63
63
  - **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..
64
64
  - **Lightweight**: All functionality is implemented within the package without heavyweight dependencies such as `djangorestframework` or `django-import-export`.
65
+ - **Optional JWT authentication**: enable token-based protection for the API with a single setting.
65
66
 
66
67
  All this makes **Treenode Framework** a prime candidate for your needs.
67
68
 
@@ -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.1
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
 
@@ -61,37 +61,41 @@ treenode/models/mixins/logical.py
61
61
  treenode/models/mixins/node.py
62
62
  treenode/models/mixins/properties.py
63
63
  treenode/models/mixins/roots.py
64
+ treenode/models/mixins/search.py
64
65
  treenode/models/mixins/siblings.py
65
66
  treenode/models/mixins/tree.py
66
67
  treenode/models/mixins/update.py
67
68
  treenode/static/.gitkeep
68
- treenode/static/css/.gitkeep
69
- treenode/static/css/tree_widget.css
70
- treenode/static/css/treenode_admin.css
71
- treenode/static/css/treenode_tabs.css
72
- treenode/static/js/.gitkeep
73
- treenode/static/js/lz-string.min.js
74
- treenode/static/js/tree_widget.js
75
- treenode/static/js/treenode_admin.js
76
- treenode/static/vendors/jquery-ui/AUTHORS.txt
77
- treenode/static/vendors/jquery-ui/LICENSE.txt
78
- treenode/static/vendors/jquery-ui/index.html
79
- treenode/static/vendors/jquery-ui/jquery-ui.css
80
- treenode/static/vendors/jquery-ui/jquery-ui.js
81
- treenode/static/vendors/jquery-ui/jquery-ui.min.css
82
- treenode/static/vendors/jquery-ui/jquery-ui.min.js
83
- treenode/static/vendors/jquery-ui/jquery-ui.structure.css
84
- treenode/static/vendors/jquery-ui/jquery-ui.structure.min.css
85
- treenode/static/vendors/jquery-ui/jquery-ui.theme.css
86
- treenode/static/vendors/jquery-ui/jquery-ui.theme.min.css
87
- treenode/static/vendors/jquery-ui/package.json
88
- treenode/static/vendors/jquery-ui/external/jquery/jquery.js
89
- treenode/static/vendors/jquery-ui/images/ui-icons_444444_256x240.png
90
- treenode/static/vendors/jquery-ui/images/ui-icons_555555_256x240.png
91
- treenode/static/vendors/jquery-ui/images/ui-icons_777620_256x240.png
92
- treenode/static/vendors/jquery-ui/images/ui-icons_777777_256x240.png
93
- treenode/static/vendors/jquery-ui/images/ui-icons_cc0000_256x240.png
94
- treenode/static/vendors/jquery-ui/images/ui-icons_ffffff_256x240.png
69
+ treenode/static/treenode/.gitkeep
70
+ treenode/static/treenode/css/.gitkeep
71
+ treenode/static/treenode/css/tree_widget.css
72
+ treenode/static/treenode/css/treenode_admin.css
73
+ treenode/static/treenode/css/treenode_tabs.css
74
+ treenode/static/treenode/js/.gitkeep
75
+ treenode/static/treenode/js/lz-string.min.js
76
+ treenode/static/treenode/js/tree_widget.js
77
+ treenode/static/treenode/js/treenode_admin.js
78
+ treenode/static/treenode/vendors/.gitkeep
79
+ treenode/static/treenode/vendors/jquery-ui/.gitkeep
80
+ treenode/static/treenode/vendors/jquery-ui/AUTHORS.txt
81
+ treenode/static/treenode/vendors/jquery-ui/LICENSE.txt
82
+ treenode/static/treenode/vendors/jquery-ui/index.html
83
+ treenode/static/treenode/vendors/jquery-ui/jquery-ui.css
84
+ treenode/static/treenode/vendors/jquery-ui/jquery-ui.js
85
+ treenode/static/treenode/vendors/jquery-ui/jquery-ui.min.css
86
+ treenode/static/treenode/vendors/jquery-ui/jquery-ui.min.js
87
+ treenode/static/treenode/vendors/jquery-ui/jquery-ui.structure.css
88
+ treenode/static/treenode/vendors/jquery-ui/jquery-ui.structure.min.css
89
+ treenode/static/treenode/vendors/jquery-ui/jquery-ui.theme.css
90
+ treenode/static/treenode/vendors/jquery-ui/jquery-ui.theme.min.css
91
+ treenode/static/treenode/vendors/jquery-ui/package.json
92
+ treenode/static/treenode/vendors/jquery-ui/external/jquery/jquery.js
93
+ treenode/static/treenode/vendors/jquery-ui/images/ui-icons_444444_256x240.png
94
+ treenode/static/treenode/vendors/jquery-ui/images/ui-icons_555555_256x240.png
95
+ treenode/static/treenode/vendors/jquery-ui/images/ui-icons_777620_256x240.png
96
+ treenode/static/treenode/vendors/jquery-ui/images/ui-icons_777777_256x240.png
97
+ treenode/static/treenode/vendors/jquery-ui/images/ui-icons_cc0000_256x240.png
98
+ treenode/static/treenode/vendors/jquery-ui/images/ui-icons_ffffff_256x240.png
95
99
  treenode/templates/.gitkeep
96
100
  treenode/templates/treenode/.gitkeep
97
101
  treenode/templates/treenode/admin/.gitkeep
@@ -103,6 +107,7 @@ treenode/templates/treenode/widgets/tree_widget.html
103
107
  treenode/templatetags/__init__.py
104
108
  treenode/templatetags/treenode_admin.py
105
109
  treenode/utils/__init__.py
110
+ treenode/utils/jwt_auth.py
106
111
  treenode/utils/db/__init__.py
107
112
  treenode/utils/db/compiler.py
108
113
  treenode/utils/db/db_vendor.py
@@ -2,3 +2,4 @@ Django>=5.0
2
2
  msgpack>=1.0.0
3
3
  openpyxl>=3.0.0
4
4
  pyyaml>=5.1
5
+ PyJWT>=2.0
@@ -97,7 +97,7 @@ class CategorySelectionForm(forms.Form):
97
97
 
98
98
  If you plan to use this widget in non-admin templates, make sure the necessary **JavaScript and CSS files** are included:
99
99
  ```html
100
- <link rel="stylesheet" href="/static/treenode/tree_widget.css">
100
+ <link rel="stylesheet" href="/static/treenode/css/tree_widget.css">
101
101
  <script src="/static/treenode/js/tree_widget.js"></script>
102
102
  ```
103
103
 
@@ -11,6 +11,7 @@ The API is divided into several logical groups, each serving a specific purpose:
11
11
  - **[Descendant Methods](#descendant-methods)** – Work with entire subtrees of nodes.
12
12
  - **[Family Methods](#family-methods)** – Retrieve and analyze relationships within a node's family (ancestors, siblings, descendants).
13
13
  - **[Node Utility Methods](#node-utility-methods)** – Additional methods for retrieving node order, paths, levels, and priorities.
14
+ - **[Search Methods](#search-methods)** – Quick node lookup utilities.
14
15
  - **[Root Node Methods](#root-node-methods)** – Manage and retrieve root nodes of trees.
15
16
  - **[Sibling Methods](#sibling-methods)** – Handle relationships between sibling nodes.
16
17
  - **[Tree Methods](#tree-methods)** – Serialize and manipulate the entire tree structure, including JSON export/import.
@@ -233,7 +234,7 @@ obj.get_depth()
233
234
  Returns number of edges on shortest path between two nodes
234
235
 
235
236
  ```python
236
- obj.distance_to(targer)
237
+ obj.distance_to(target)
237
238
  ```
238
239
 
239
240
  #### get_level
@@ -351,6 +352,28 @@ obj.get_root_pk()
351
352
 
352
353
  ---
353
354
 
355
+ ### Search Methods
356
+ These methods provide convenient helpers for finding nodes by
357
+ breadcrumb paths or within a subtree.
358
+
359
+ #### find_by_path
360
+ Find a node by a path previously generated via `get_breadcrumbs`.
361
+ ```python
362
+ cls.find_by_path("root/A/D", attr="name", delimiter="/")
363
+ ```
364
+
365
+ Returns the matching node or `None`.
366
+
367
+ #### find_in_subtree
368
+ Search for a node among the descendants of a parent node.
369
+ ```python
370
+ cls.find_in_subtree(parent, "D", attr="name")
371
+ ```
372
+
373
+ Returns the first found node or `None`.
374
+
375
+ ---
376
+
354
377
  ### Root Node Methods
355
378
  These methods allow managing **root nodes** efficiently. They provide retrieval, counting, and manipulation of the first and last root nodes in the tree.
356
379
 
@@ -111,9 +111,9 @@ No complicated payloads. No custom formats. **TreeNode Framework** believes tha
111
111
  ### Basic Access Control
112
112
  TreeNode Framework follows an API-First philosophy: API endpoints are generated automatically for each tree model, without the need to manually register views or routes.
113
113
 
114
- However, API protection is currently basic — based on login sessions using Django's standard authentication system (login_required). This is simple but effective for many internal or admin-side applications.
114
+ By default, API protection uses Django's login sessions (`login_required`).
115
115
 
116
- In the future, full token-based authentication (e.g., JWT) will be introduced for more robust and flexible security.
116
+ Starting from version 3.0.9 you can enable JWT authentication by setting `TREENODE_API_USE_JWT = True` in your project settings. In this mode the API expects an `Authorization: Bearer <token>` header with a token signed using your `SECRET_KEY`.
117
117
 
118
118
  #### How to Secure Your API Step-by-Step
119
119
  Since TreeNode Framework does not provide an authentication system itself, you need to set up basic login endpoints in your project.
@@ -77,7 +77,7 @@ python manage.py shell
77
77
  Then execute:
78
78
 
79
79
  ```python
80
- YourTreeNodeModel.rebuild()
80
+ YourTreeNodeModel.update_tree()
81
81
  ```
82
82
 
83
83
  !!! note
@@ -15,7 +15,7 @@ The 3.x release series will focus on strengthening TreeNode Framework in terms o
15
15
  - Provide a fallback auto-run mode for DEBUG environments (using `atexit` or thread-based handler).
16
16
  - Ensure task queue consistency across multiple WSGI workers or scripts.
17
17
 
18
- * **Version 3.2 — JWT Authentication for API**
18
+ * **Version 3.2 — JWT Authentication for API** *(implemented)*
19
19
 
20
20
  - Introduce optional JWT-based token authentication for the auto-generated API.
21
21
  - Allow easy activation through a single setting (`TREENODE_API_USE_JWT = True`).
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "django-fast-treenode"
7
- version = "3.0.8"
7
+ version = "3.2.1"
8
8
  description = "Treenode Framework for supporting tree (hierarchical) data structure in Django projects"
9
9
  readme = "README.md"
10
10
  authors = [{ name = "Timur Kady", email = "timurkady@yandex.com" }]
@@ -15,6 +15,7 @@ dependencies = [
15
15
  'msgpack>=1.0.0',
16
16
  'openpyxl>=3.0.0',
17
17
  'pyyaml>=5.1',
18
+ 'PyJWT>=2.0',
18
19
  ]
19
20
  classifiers = [
20
21
  'Development Status :: 5 - Production/Stable',
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='django-fast-treenode',
5
- version='3.0.8',
5
+ version='3.2.1',
6
6
  description='Treenode Framework for supporting tree (hierarchical) data structure in Django projects',
7
7
  long_description=open('README.md', encoding='utf-8').read(),
8
8
  long_description_content_type='text/markdown',
@@ -18,6 +18,7 @@ setup(
18
18
  'msgpack>=1.0.0',
19
19
  'openpyxl>=3.0.0',
20
20
  'pyyaml>=5.1',
21
+ 'PyJWT>=2.0',
21
22
  ],
22
23
  classifiers=[
23
24
  'Development Status :: 5 - Production/Stable',
@@ -42,3 +43,4 @@ setup(
42
43
  python_requires='>=3.9',
43
44
  )
44
45
 
46
+
@@ -89,11 +89,18 @@ class TreeNodeModelTests(TestCase):
89
89
 
90
90
  def test_delete_subtree(self):
91
91
  self.a.delete(cascade=False)
92
-
93
- tree_data = TestModel.get_tree_json()
94
-
95
92
  self.root.check_tree_integrity()
96
93
  qs = TestModel.objects.filter(pk__in=[self.a.pk, self.c.pk]).all()
97
-
98
94
  self.assertFalse(TestModel.objects.filter(pk=self.a.pk).exists())
99
95
  self.assertTrue(TestModel.objects.filter(pk=self.c.pk).exists())
96
+
97
+ # --- 6. Search ----------------------------------------------------------
98
+
99
+ def test_find_by_path(self):
100
+ path = "/".join(self.d.get_breadcrumbs(attr="name"))
101
+ node = TestModel.find_by_path(path, attr="name", delimiter="/")
102
+ self.assertEqual(node, self.d)
103
+
104
+ def test_find_in_subtree(self):
105
+ node = TestModel.find_in_subtree(self.a, "D", attr="name")
106
+ self.assertEqual(node, self.d)
@@ -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):
@@ -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)
@@ -137,7 +137,25 @@ class TreeTaskQueue:
137
137
  if not merged:
138
138
  result_set.add(current)
139
139
 
140
- return [{"mode": "update", "parent_id": pk} for pk in sorted(result_set)]
140
+ if not result_set:
141
+ return []
142
+
143
+ depth_lookup = {}
144
+ if result_set:
145
+ with connection.cursor() as cursor:
146
+ format_ids = ', '.join(['%s'] * len(result_set))
147
+ sql = f"""
148
+ SELECT id, _depth
149
+ FROM {self.model._meta.db_table}
150
+ WHERE id IN ({format_ids})
151
+ """
152
+ cursor.execute(sql, list(result_set))
153
+ for node_id, depth in cursor.fetchall():
154
+ depth_lookup[node_id] = depth
155
+
156
+
157
+ ordered_pks = sorted(result_set, key=lambda pk: (depth_lookup.get(pk, 0), pk))
158
+ return [{"mode": "update", "parent_id": pk} for pk in ordered_pks]
141
159
 
142
160
  def _get_root_ids(self):
143
161
  """Return root node IDs."""
@@ -7,6 +7,7 @@ from .family import TreeNodeFamilyMixin
7
7
  from .logical import TreeNodeLogicalMixin
8
8
  from .node import TreeNodeNodeMixin
9
9
  from .properties import TreeNodePropertiesMixin
10
+ from .search import TreeNodeSearchMixin
10
11
  from .roots import TreeNodeRootsMixin
11
12
  from .siblings import TreeNodeSiblingsMixin
12
13
  from .tree import TreeNodeTreeMixin
@@ -16,7 +17,8 @@ from .update import RawSQLMixin
16
17
  __all__ = [
17
18
  "TreeNodeAncestorsMixin", "TreeNodeChildrenMixin", "TreeNodeFamilyMixin",
18
19
  "TreeNodeDescendantsMixin", "TreeNodeLogicalMixin", "TreeNodeNodeMixin",
19
- "TreeNodePropertiesMixin", "TreeNodeRootsMixin", "TreeNodeSiblingsMixin",
20
+ "TreeNodeSearchMixin", "TreeNodePropertiesMixin", "TreeNodeRootsMixin",
21
+ "TreeNodeSiblingsMixin", "TreeNodeTreeMixin", "RawSQLMixin"
20
22
  "TreeNodeTreeMixin", "RawSQLMixin"
21
23
  ]
22
24
 
@@ -86,4 +86,4 @@ class TreeNodeChildrenMixin(models.Model):
86
86
  """Get the last child node or None if it has no children."""
87
87
  return self.get_children_queryset().last()
88
88
 
89
- # The End
89
+ # The End
@@ -74,4 +74,4 @@ class TreeNodeDescendantsMixin(models.Model):
74
74
  )
75
75
 
76
76
 
77
- # The End
77
+ # The End
@@ -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)."""
@@ -137,4 +137,4 @@ class TreeNodeRootsMixin(models.Model):
137
137
  cursor.execute(query, params)
138
138
 
139
139
 
140
- # The End
140
+ # The End
@@ -0,0 +1,55 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ TreeNode Search Mixin
4
+
5
+ Version: 3.2.1
6
+ Author: Timur Kady
7
+ Email: timurkady@yandex.com
8
+ """
9
+
10
+ from django.db import models
11
+
12
+
13
+ class TreeNodeSearchMixin(models.Model):
14
+ """Mixin that provides helper methods for quick node lookup."""
15
+
16
+ class Meta:
17
+ """Mixin Meta Class."""
18
+ abstract = True
19
+
20
+ @classmethod
21
+ def find_by_path(cls, path, attr="id", delimiter="/"):
22
+ """Return a node referenced by breadcrumbs path.
23
+
24
+ The ``path`` argument may be a string or an iterable of breadcrumb
25
+ values previously produced by :py:meth:`get_breadcrumbs`.
26
+ If the path cannot be resolved, ``None`` is returned.
27
+ """
28
+ if path is None:
29
+ return None
30
+
31
+ if not isinstance(path, (list, tuple)):
32
+ path = [p for p in str(path).strip(delimiter).split(delimiter) if p]
33
+
34
+ parent = None
35
+ for token in path:
36
+ lookup = {attr: token}
37
+ if parent is None:
38
+ qs = cls.objects.filter(parent_id__isnull=True, **lookup)
39
+ else:
40
+ qs = cls.objects.filter(parent=parent, **lookup)
41
+ parent = qs.first()
42
+ if parent is None:
43
+ return None
44
+ return parent
45
+
46
+ @classmethod
47
+ def find_in_subtree(cls, parent, value, attr="id"):
48
+ """Search ``parent`` descendants for attribute ``attr`` equal to ``value``."""
49
+ if parent is None:
50
+ return None
51
+ prefix = parent.get_order() + "."
52
+ return cls.objects.filter(_path__startswith=prefix, **{attr: value}).first()
53
+
54
+
55
+ # The End
@@ -99,4 +99,4 @@ class TreeNodeSiblingsMixin(models.Model):
99
99
  qs = self._meta.model.objects.filter(parent_id=self._parent_id)
100
100
  return qs.last()
101
101
 
102
- # The End
102
+ # The End