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.
- {django_fast_treenode-3.0.8/django_fast_treenode.egg-info → django_fast_treenode-3.2.1}/PKG-INFO +3 -1
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/README.md +1 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1/django_fast_treenode.egg-info}/PKG-INFO +3 -1
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/django_fast_treenode.egg-info/SOURCES.txt +32 -27
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/django_fast_treenode.egg-info/requires.txt +1 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/admin.md +1 -1
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/api.md +24 -1
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/apifirst.md +2 -2
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/migration.md +1 -1
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/roadmap.md +1 -1
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/pyproject.toml +2 -1
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/setup.py +3 -1
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/tests/test_suite.py +11 -4
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/admin/admin.py +5 -5
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/admin/mixin.py +6 -6
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/managers/managers.py +1 -1
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/managers/tasks.py +19 -1
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/mixins/__init__.py +3 -1
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/mixins/children.py +1 -1
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/mixins/descendants.py +1 -1
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/mixins/node.py +4 -4
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/mixins/roots.py +1 -1
- django_fast_treenode-3.2.1/treenode/models/mixins/search.py +55 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/mixins/siblings.py +1 -1
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/models.py +31 -14
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/settings.py +4 -1
- django_fast_treenode-3.2.1/treenode/static/treenode/vendors/jquery-ui/.gitkeep +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/templates/treenode/admin/treenode_import_export.html +3 -2
- django_fast_treenode-3.2.1/treenode/templatetags/__init__.py +0 -0
- django_fast_treenode-3.2.1/treenode/utils/__init__.py +0 -0
- django_fast_treenode-3.2.1/treenode/utils/db/compiler.py +72 -0
- django_fast_treenode-3.2.1/treenode/utils/jwt_auth.py +25 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/version.py +2 -2
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/views/autoapi.py +95 -91
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/widgets.py +2 -2
- django_fast_treenode-3.0.8/treenode/utils/db/compiler.py +0 -113
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/LICENSE +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/MANIFEST.in +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/django_fast_treenode.egg-info/dependency_links.txt +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/django_fast_treenode.egg-info/top_level.txt +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/.gitignore +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/.nojekyll +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/about.md +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/cache.md +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/customization.md +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/dnd.md +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/import_export.md +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/index.md +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/insert-after.jpg +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/insert-as-child.jpg +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/installation.md +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/models.md +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/requirements.txt +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/docs/using.md +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/setup.cfg +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/__init__.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/admin/__init__.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/admin/changelist.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/admin/exporter.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/admin/importer.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/apps.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/cache.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/forms.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/managers/__init__.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/managers/queries.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/__init__.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/decorators.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/factory.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/mixins/ancestors.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/mixins/family.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/mixins/logical.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/mixins/properties.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/mixins/tree.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/mixins/update.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/signals.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/static/.gitkeep +0 -0
- /django_fast_treenode-3.0.8/treenode/templatetags/__init__.py → /django_fast_treenode-3.2.1/treenode/static/treenode/.gitkeep +0 -0
- {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/css/.gitkeep +0 -0
- {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/css/tree_widget.css +0 -0
- {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/css/treenode_admin.css +0 -0
- {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/css/treenode_tabs.css +0 -0
- {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/js/.gitkeep +0 -0
- {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/js/lz-string.min.js +0 -0
- {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/js/tree_widget.js +0 -0
- {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/js/treenode_admin.js +0 -0
- /django_fast_treenode-3.0.8/treenode/utils/__init__.py → /django_fast_treenode-3.2.1/treenode/static/treenode/vendors/.gitkeep +0 -0
- {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/vendors/jquery-ui/AUTHORS.txt +0 -0
- {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/vendors/jquery-ui/LICENSE.txt +0 -0
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/vendors/jquery-ui/index.html +0 -0
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {django_fast_treenode-3.0.8/treenode/static → django_fast_treenode-3.2.1/treenode/static/treenode}/vendors/jquery-ui/package.json +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/templates/.gitkeep +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/templates/treenode/.gitkeep +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/templates/treenode/admin/.gitkeep +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/templates/treenode/admin/treenode_ajax_rows.html +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/templates/treenode/admin/treenode_changelist.html +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/templates/treenode/admin/treenode_rows.html +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/templates/treenode/widgets/tree_widget.html +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/templatetags/treenode_admin.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/tests.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/urls.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/utils/db/__init__.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/utils/db/db_vendor.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/utils/db/service.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/utils/db/sqlcompat.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/utils/db/sqlquery.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/views/__init__.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/views/autocomplete.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/views/children.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/views/common.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/views/crud.py +0 -0
- {django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/views/search.py +0 -0
{django_fast_treenode-3.0.8/django_fast_treenode.egg-info → django_fast_treenode-3.2.1}/PKG-INFO
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: django-fast-treenode
|
3
|
-
Version: 3.
|
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
|
|
{django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1/django_fast_treenode.egg-info}/PKG-INFO
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: django-fast-treenode
|
3
|
-
Version: 3.
|
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
|
|
{django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/django_fast_treenode.egg-info/SOURCES.txt
RENAMED
@@ -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/
|
69
|
-
treenode/static/
|
70
|
-
treenode/static/css/
|
71
|
-
treenode/static/css/
|
72
|
-
treenode/static/
|
73
|
-
treenode/static/
|
74
|
-
treenode/static/js/
|
75
|
-
treenode/static/js/
|
76
|
-
treenode/static/
|
77
|
-
treenode/static/vendors
|
78
|
-
treenode/static/vendors/jquery-ui
|
79
|
-
treenode/static/vendors/jquery-ui/
|
80
|
-
treenode/static/vendors/jquery-ui/
|
81
|
-
treenode/static/vendors/jquery-ui/
|
82
|
-
treenode/static/vendors/jquery-ui/jquery-ui.
|
83
|
-
treenode/static/vendors/jquery-ui/jquery-ui.
|
84
|
-
treenode/static/vendors/jquery-ui/jquery-ui.
|
85
|
-
treenode/static/vendors/jquery-ui/jquery-ui.
|
86
|
-
treenode/static/vendors/jquery-ui/jquery-ui.
|
87
|
-
treenode/static/vendors/jquery-ui/
|
88
|
-
treenode/static/vendors/jquery-ui/
|
89
|
-
treenode/static/vendors/jquery-ui/
|
90
|
-
treenode/static/vendors/jquery-ui/
|
91
|
-
treenode/static/vendors/jquery-ui/
|
92
|
-
treenode/static/vendors/jquery-ui/images/ui-
|
93
|
-
treenode/static/vendors/jquery-ui/images/ui-
|
94
|
-
treenode/static/vendors/jquery-ui/images/ui-
|
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
|
@@ -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(
|
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
|
-
|
114
|
+
By default, API protection uses Django's login sessions (`login_required`).
|
115
115
|
|
116
|
-
|
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.
|
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.
|
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
|
-
"""
|
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
|
-
|
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
|
-
|
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
|
-
|
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."""
|
{django_fast_treenode-3.0.8 → django_fast_treenode-3.2.1}/treenode/models/mixins/__init__.py
RENAMED
@@ -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
|
-
"
|
20
|
+
"TreeNodeSearchMixin", "TreeNodePropertiesMixin", "TreeNodeRootsMixin",
|
21
|
+
"TreeNodeSiblingsMixin", "TreeNodeTreeMixin", "RawSQLMixin"
|
20
22
|
"TreeNodeTreeMixin", "RawSQLMixin"
|
21
23
|
]
|
22
24
|
|
@@ -40,18 +40,18 @@ class TreeNodeNodeMixin(models.Model):
|
|
40
40
|
self.refresh()
|
41
41
|
return self._depth
|
42
42
|
|
43
|
-
def distance_to(self,
|
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
|
-
|
46
|
+
target_path = target.query(objects='ancestors')
|
47
47
|
|
48
48
|
i = 0
|
49
|
-
for a, b in zip(self_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(
|
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)."""
|
@@ -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
|