django-fast-treenode 3.0.8__tar.gz → 3.2.0__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 (124) hide show
  1. {django_fast_treenode-3.0.8/django_fast_treenode.egg-info → django_fast_treenode-3.2.0}/PKG-INFO +3 -1
  2. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/README.md +1 -0
  3. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0/django_fast_treenode.egg-info}/PKG-INFO +3 -1
  4. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/django_fast_treenode.egg-info/SOURCES.txt +31 -27
  5. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/django_fast_treenode.egg-info/requires.txt +1 -0
  6. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/docs/admin.md +1 -1
  7. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/docs/api.md +1 -1
  8. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/docs/apifirst.md +2 -2
  9. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/docs/roadmap.md +1 -1
  10. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/pyproject.toml +2 -1
  11. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/setup.py +3 -1
  12. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/tests/test_suite.py +0 -4
  13. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/admin/admin.py +5 -5
  14. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/admin/mixin.py +6 -6
  15. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/managers/managers.py +1 -1
  16. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/models/mixins/node.py +4 -4
  17. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/models/models.py +14 -9
  18. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/settings.py +4 -1
  19. django_fast_treenode-3.2.0/treenode/static/treenode/vendors/jquery-ui/.gitkeep +0 -0
  20. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/templates/treenode/admin/treenode_import_export.html +3 -2
  21. django_fast_treenode-3.2.0/treenode/templatetags/__init__.py +0 -0
  22. django_fast_treenode-3.2.0/treenode/utils/__init__.py +0 -0
  23. django_fast_treenode-3.2.0/treenode/utils/jwt_auth.py +25 -0
  24. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/version.py +2 -2
  25. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/views/autoapi.py +95 -91
  26. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/widgets.py +2 -2
  27. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/LICENSE +0 -0
  28. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/MANIFEST.in +0 -0
  29. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/django_fast_treenode.egg-info/dependency_links.txt +0 -0
  30. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/django_fast_treenode.egg-info/top_level.txt +0 -0
  31. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/docs/.gitignore +0 -0
  32. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/docs/.nojekyll +0 -0
  33. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/docs/about.md +0 -0
  34. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/docs/cache.md +0 -0
  35. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/docs/customization.md +0 -0
  36. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/docs/dnd.md +0 -0
  37. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/docs/import_export.md +0 -0
  38. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/docs/index.md +0 -0
  39. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/docs/insert-after.jpg +0 -0
  40. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/docs/insert-as-child.jpg +0 -0
  41. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/docs/installation.md +0 -0
  42. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/docs/migration.md +0 -0
  43. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/docs/models.md +0 -0
  44. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/docs/requirements.txt +0 -0
  45. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/docs/using.md +0 -0
  46. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/setup.cfg +0 -0
  47. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/__init__.py +0 -0
  48. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/admin/__init__.py +0 -0
  49. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/admin/changelist.py +0 -0
  50. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/admin/exporter.py +0 -0
  51. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/admin/importer.py +0 -0
  52. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/apps.py +0 -0
  53. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/cache.py +0 -0
  54. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/forms.py +0 -0
  55. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/managers/__init__.py +0 -0
  56. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/managers/queries.py +0 -0
  57. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/managers/tasks.py +0 -0
  58. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/models/__init__.py +0 -0
  59. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/models/decorators.py +0 -0
  60. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/models/factory.py +0 -0
  61. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/models/mixins/__init__.py +0 -0
  62. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/models/mixins/ancestors.py +0 -0
  63. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/models/mixins/children.py +0 -0
  64. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/models/mixins/descendants.py +0 -0
  65. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/models/mixins/family.py +0 -0
  66. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/models/mixins/logical.py +0 -0
  67. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/models/mixins/properties.py +0 -0
  68. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/models/mixins/roots.py +0 -0
  69. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/models/mixins/siblings.py +0 -0
  70. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/models/mixins/tree.py +0 -0
  71. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/models/mixins/update.py +0 -0
  72. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/signals.py +0 -0
  73. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/static/.gitkeep +0 -0
  74. /django_fast_treenode-3.0.8/treenode/templatetags/__init__.py → /django_fast_treenode-3.2.0/treenode/static/treenode/.gitkeep +0 -0
  75. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/css/.gitkeep +0 -0
  76. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/css/tree_widget.css +0 -0
  77. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/css/treenode_admin.css +0 -0
  78. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/css/treenode_tabs.css +0 -0
  79. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/js/.gitkeep +0 -0
  80. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/js/lz-string.min.js +0 -0
  81. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/js/tree_widget.js +0 -0
  82. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/js/treenode_admin.js +0 -0
  83. /django_fast_treenode-3.0.8/treenode/utils/__init__.py → /django_fast_treenode-3.2.0/treenode/static/treenode/vendors/.gitkeep +0 -0
  84. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/vendors/jquery-ui/AUTHORS.txt +0 -0
  85. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/vendors/jquery-ui/LICENSE.txt +0 -0
  86. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/vendors/jquery-ui/external/jquery/jquery.js +0 -0
  87. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/vendors/jquery-ui/images/ui-icons_444444_256x240.png +0 -0
  88. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/vendors/jquery-ui/images/ui-icons_555555_256x240.png +0 -0
  89. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/vendors/jquery-ui/images/ui-icons_777620_256x240.png +0 -0
  90. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/vendors/jquery-ui/images/ui-icons_777777_256x240.png +0 -0
  91. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/vendors/jquery-ui/images/ui-icons_cc0000_256x240.png +0 -0
  92. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/vendors/jquery-ui/images/ui-icons_ffffff_256x240.png +0 -0
  93. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/vendors/jquery-ui/index.html +0 -0
  94. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/vendors/jquery-ui/jquery-ui.css +0 -0
  95. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/vendors/jquery-ui/jquery-ui.js +0 -0
  96. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/vendors/jquery-ui/jquery-ui.min.css +0 -0
  97. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/vendors/jquery-ui/jquery-ui.min.js +0 -0
  98. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/vendors/jquery-ui/jquery-ui.structure.css +0 -0
  99. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/vendors/jquery-ui/jquery-ui.structure.min.css +0 -0
  100. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/vendors/jquery-ui/jquery-ui.theme.css +0 -0
  101. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/vendors/jquery-ui/jquery-ui.theme.min.css +0 -0
  102. {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.0/treenode/static/treenode}/vendors/jquery-ui/package.json +0 -0
  103. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/templates/.gitkeep +0 -0
  104. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/templates/treenode/.gitkeep +0 -0
  105. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/templates/treenode/admin/.gitkeep +0 -0
  106. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/templates/treenode/admin/treenode_ajax_rows.html +0 -0
  107. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/templates/treenode/admin/treenode_changelist.html +0 -0
  108. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/templates/treenode/admin/treenode_rows.html +0 -0
  109. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/templates/treenode/widgets/tree_widget.html +0 -0
  110. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/templatetags/treenode_admin.py +0 -0
  111. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/tests.py +0 -0
  112. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/urls.py +0 -0
  113. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/utils/db/__init__.py +0 -0
  114. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/utils/db/compiler.py +0 -0
  115. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/utils/db/db_vendor.py +0 -0
  116. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/utils/db/service.py +0 -0
  117. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/utils/db/sqlcompat.py +0 -0
  118. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/utils/db/sqlquery.py +0 -0
  119. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/views/__init__.py +0 -0
  120. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/views/autocomplete.py +0 -0
  121. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/views/children.py +0 -0
  122. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/views/common.py +0 -0
  123. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/treenode/views/crud.py +0 -0
  124. {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.0}/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.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
 
@@ -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.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
 
@@ -65,33 +65,36 @@ treenode/models/mixins/siblings.py
65
65
  treenode/models/mixins/tree.py
66
66
  treenode/models/mixins/update.py
67
67
  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
68
+ treenode/static/treenode/.gitkeep
69
+ treenode/static/treenode/css/.gitkeep
70
+ treenode/static/treenode/css/tree_widget.css
71
+ treenode/static/treenode/css/treenode_admin.css
72
+ treenode/static/treenode/css/treenode_tabs.css
73
+ treenode/static/treenode/js/.gitkeep
74
+ treenode/static/treenode/js/lz-string.min.js
75
+ treenode/static/treenode/js/tree_widget.js
76
+ treenode/static/treenode/js/treenode_admin.js
77
+ treenode/static/treenode/vendors/.gitkeep
78
+ treenode/static/treenode/vendors/jquery-ui/.gitkeep
79
+ treenode/static/treenode/vendors/jquery-ui/AUTHORS.txt
80
+ treenode/static/treenode/vendors/jquery-ui/LICENSE.txt
81
+ treenode/static/treenode/vendors/jquery-ui/index.html
82
+ treenode/static/treenode/vendors/jquery-ui/jquery-ui.css
83
+ treenode/static/treenode/vendors/jquery-ui/jquery-ui.js
84
+ treenode/static/treenode/vendors/jquery-ui/jquery-ui.min.css
85
+ treenode/static/treenode/vendors/jquery-ui/jquery-ui.min.js
86
+ treenode/static/treenode/vendors/jquery-ui/jquery-ui.structure.css
87
+ treenode/static/treenode/vendors/jquery-ui/jquery-ui.structure.min.css
88
+ treenode/static/treenode/vendors/jquery-ui/jquery-ui.theme.css
89
+ treenode/static/treenode/vendors/jquery-ui/jquery-ui.theme.min.css
90
+ treenode/static/treenode/vendors/jquery-ui/package.json
91
+ treenode/static/treenode/vendors/jquery-ui/external/jquery/jquery.js
92
+ treenode/static/treenode/vendors/jquery-ui/images/ui-icons_444444_256x240.png
93
+ treenode/static/treenode/vendors/jquery-ui/images/ui-icons_555555_256x240.png
94
+ treenode/static/treenode/vendors/jquery-ui/images/ui-icons_777620_256x240.png
95
+ treenode/static/treenode/vendors/jquery-ui/images/ui-icons_777777_256x240.png
96
+ treenode/static/treenode/vendors/jquery-ui/images/ui-icons_cc0000_256x240.png
97
+ treenode/static/treenode/vendors/jquery-ui/images/ui-icons_ffffff_256x240.png
95
98
  treenode/templates/.gitkeep
96
99
  treenode/templates/treenode/.gitkeep
97
100
  treenode/templates/treenode/admin/.gitkeep
@@ -103,6 +106,7 @@ treenode/templates/treenode/widgets/tree_widget.html
103
106
  treenode/templatetags/__init__.py
104
107
  treenode/templatetags/treenode_admin.py
105
108
  treenode/utils/__init__.py
109
+ treenode/utils/jwt_auth.py
106
110
  treenode/utils/db/__init__.py
107
111
  treenode/utils/db/compiler.py
108
112
  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
 
@@ -233,7 +233,7 @@ obj.get_depth()
233
233
  Returns number of edges on shortest path between two nodes
234
234
 
235
235
  ```python
236
- obj.distance_to(targer)
236
+ obj.distance_to(target)
237
237
  ```
238
238
 
239
239
  #### get_level
@@ -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.
@@ -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.0"
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.0',
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,7 @@ 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())
@@ -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)
@@ -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)."""
@@ -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
 
@@ -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
@@ -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 %}
File without changes
@@ -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
+
@@ -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'
@@ -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
@@ -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."""