django-fast-treenode 2.0.11__py3-none-any.whl → 2.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. {django_fast_treenode-2.0.11.dist-info → django_fast_treenode-2.1.1.dist-info}/LICENSE +2 -2
  2. django_fast_treenode-2.1.1.dist-info/METADATA +158 -0
  3. django_fast_treenode-2.1.1.dist-info/RECORD +64 -0
  4. {django_fast_treenode-2.0.11.dist-info → django_fast_treenode-2.1.1.dist-info}/WHEEL +1 -1
  5. treenode/admin/__init__.py +9 -0
  6. treenode/admin/admin.py +295 -0
  7. treenode/admin/changelist.py +65 -0
  8. treenode/admin/mixins.py +302 -0
  9. treenode/apps.py +12 -1
  10. treenode/cache.py +2 -2
  11. treenode/forms.py +8 -10
  12. treenode/managers/__init__.py +21 -0
  13. treenode/managers/adjacency.py +203 -0
  14. treenode/managers/closure.py +278 -0
  15. treenode/models/__init__.py +2 -1
  16. treenode/models/adjacency.py +343 -0
  17. treenode/models/classproperty.py +3 -0
  18. treenode/models/closure.py +23 -24
  19. treenode/models/factory.py +12 -2
  20. treenode/models/mixins/__init__.py +23 -0
  21. treenode/models/mixins/ancestors.py +65 -0
  22. treenode/models/mixins/children.py +81 -0
  23. treenode/models/mixins/descendants.py +66 -0
  24. treenode/models/mixins/family.py +63 -0
  25. treenode/models/mixins/logical.py +68 -0
  26. treenode/models/mixins/node.py +210 -0
  27. treenode/models/mixins/properties.py +156 -0
  28. treenode/models/mixins/roots.py +96 -0
  29. treenode/models/mixins/siblings.py +99 -0
  30. treenode/models/mixins/tree.py +344 -0
  31. treenode/signals.py +26 -0
  32. treenode/static/treenode/css/tree_widget.css +201 -31
  33. treenode/static/treenode/css/treenode_admin.css +48 -41
  34. treenode/static/treenode/js/tree_widget.js +269 -131
  35. treenode/static/treenode/js/treenode_admin.js +131 -171
  36. treenode/templates/admin/tree_node_changelist.html +6 -0
  37. treenode/templates/admin/treenode_ajax_rows.html +7 -0
  38. treenode/tests/tests.py +488 -0
  39. treenode/urls.py +10 -6
  40. treenode/utils/__init__.py +2 -0
  41. treenode/utils/aid.py +46 -0
  42. treenode/utils/base16.py +38 -0
  43. treenode/utils/base36.py +3 -1
  44. treenode/utils/db.py +116 -0
  45. treenode/utils/exporter.py +2 -0
  46. treenode/utils/importer.py +0 -1
  47. treenode/utils/radix.py +61 -0
  48. treenode/version.py +2 -2
  49. treenode/views.py +118 -43
  50. treenode/widgets.py +91 -43
  51. django_fast_treenode-2.0.11.dist-info/METADATA +0 -698
  52. django_fast_treenode-2.0.11.dist-info/RECORD +0 -42
  53. treenode/admin.py +0 -439
  54. treenode/docs/Documentation +0 -636
  55. treenode/managers.py +0 -419
  56. treenode/models/proxy.py +0 -669
  57. {django_fast_treenode-2.0.11.dist-info → django_fast_treenode-2.1.1.dist-info}/top_level.txt +0 -0
@@ -1,636 +0,0 @@
1
- # Django-fast-treenode
2
- __Combination of Adjacency List and Closure Table__
3
-
4
- ## Functions
5
- Application for supporting tree (hierarchical) data structure in Django projects
6
- * fast: the fastest of the two methods is used to process requests, combining the advantages of an **Adjacency Table** and a **Closure Table**,
7
- * even faster: the main resource-intensive operations are **cached**; **bulk operations** are used for inserts and changes,
8
- * synchronized: model instances in memory are automatically updated,
9
- * compatibility: you can easily add a tree node to existing projects using TreeNode without changing the code,
10
- * no dependencies,
11
- * easy setup: just extend the abstract model/model-admin,
12
- * admin integration: visualization options (accordion, breadcrumbs or padding),
13
- * widget: Built-in Select2-to-Tree extends Select2 to support arbitrary nesting levels.
14
-
15
- ## Debut idea
16
- This is a modification of the reusable [django-treenode](https://github.com/fabiocaccamo/django-treenode) application developed by [Fabio Caccamo](https://github.com/fabiocaccamo).
17
- The original application has significant advantages over other analogues, and indeed, is one of the best implementations of support for hierarchical structures for Django.
18
-
19
- Fabio's idea was to use the Adjacency List method to store the data tree. The most probable and time-consuming requests are calculated in advance and stored in the database. Also, most requests are cached. As a result, query processing is carried out in one call to the database or without it at all.
20
-
21
- However, this application has a number of undeniable shortcomings:
22
- * the selected pre-calculations scheme entails high costs for adding a new element;
23
- * inserting new elements uses signals, which leads to failures when using bulk-operations;
24
- * the problem of ordering elements by priority inside the parent node has not been resolved.
25
-
26
- My idea was to solve these problems by combining the adjacency list with the Closure Table. Main advantages:
27
- * the Closure Model is generated automatically;
28
- * maintained compatibility with the original package at the level of documented functions;
29
- * most requests are satisfied in one call to the database;
30
- * inserting a new element takes two calls to the database without signals usage;
31
- * bulk-operations are supported;
32
- * the cost of creating a new dependency is reduced many times;
33
- * useful functionality added for some methods (e.g. the `include_self=False` and `depth` parameters has been added to functions that return lists/querysets);
34
- * additionally, the package includes a tree view widget for the `tn_parent` field in the change form.
35
-
36
- Of course, at large levels of nesting, the use of the Closure Table leads to an increase in resource costs. However, the combined approach still outperforms both the original application and other available Django solutions in terms of performance, especially in large trees with over 100k nodes.
37
-
38
- ## Theory
39
- You can get a basic understanding of what is a Closure Table from:
40
- * [presentation](https://www.slideshare.net/billkarwin/models-for-hierarchical-data) by Bill Karwin;
41
- * [article](https://dirtsimple.org/2010/11/simplest-way-to-do-tree-based-queries.html) by blogger Dirt Simple;
42
- * [article](https://towardsdatascience.com/closure-table-pattern-to-model-hierarchies-in-nosql-c1be6a87e05b) by Andriy Zabavskyy.
43
-
44
- You can easily find additional information on your own on the Internet.
45
-
46
- ## Quick start
47
- 1. Run ```pip install django-fast-treenode```
48
- 2. Add ```treenode``` to ```settings.INSTALLED_APPS```
49
- 3. Make your model inherit from ```treenode.models.TreeNodeModel``` (described below)
50
- 4. Make your model-admin inherit from ```treenode.admin.TreeNodeModelAdmin``` (described below)
51
- 5. Run python manage.py makemigrations and ```python manage.py migrate```
52
-
53
- For more information on migrating from the **django-treenode** package and upgrading to version 2.0, see [below](#migration-guide).
54
-
55
- ## Configuration
56
- ### `models.py`
57
- Make your model class inherit from `treenode.models.TreeNodeModel`:
58
-
59
- ```python
60
- from django.db import models
61
- from treenode.models import TreeNodeModel
62
-
63
-
64
- class Category(TreeNodeModel):
65
-
66
- # the field used to display the model instance
67
- # default value 'pk'
68
- treenode_display_field = "name"
69
-
70
- name = models.CharField(max_length=50)
71
-
72
- class Meta(TreeNodeModel.Meta):
73
- verbose_name = "Category"
74
- verbose_name_plural = "Categories"
75
- ```
76
-
77
- The `TreeNodeModel` abstract class adds many fields (prefixed with `tn_` to prevent direct access) and public methods to your models.
78
-
79
- ### `admin.py`
80
- Make your model-admin class inherit from `treenode.admin.TreeNodeModelAdmin`.
81
-
82
- ```python
83
- from django.contrib import admin
84
-
85
- from treenode.admin import TreeNodeModelAdmin
86
- from treenode.forms import TreeNodeForm
87
-
88
- from .models import Category
89
-
90
-
91
- class CategoryAdmin(TreeNodeModelAdmin):
92
-
93
- # set the changelist display mode: 'accordion', 'breadcrumbs' or 'indentation' (default)
94
- # when changelist results are filtered by a querystring,
95
- # 'breadcrumbs' mode will be used (to preserve data display integrity)
96
- treenode_display_mode = TreeNodeModelAdmin.TREENODE_DISPLAY_MODE_ACCORDION
97
- # treenode_display_mode = TreeNodeModelAdmin.TREENODE_DISPLAY_MODE_BREADCRUMBS
98
- # treenode_display_mode = TreeNodeModelAdmin.TREENODE_DISPLAY_MODE_INDENTATION
99
-
100
- # use TreeNodeForm to automatically exclude invalid parent choices
101
- form = TreeNodeForm
102
-
103
- admin.site.register(Category, CategoryAdmin)
104
- ```
105
-
106
- ---
107
-
108
- ### `settings.py`
109
- You can use a custom cache backend by adding a `treenode` entry to `settings.CACHES`, otherwise the default cache backend will be used.
110
-
111
- ```python
112
- CACHES = {
113
- "default": {
114
- "BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
115
- "LOCATION": "...",
116
- },
117
- "treenode": {
118
- "BACKEND": "django.core.cache.backends.locmem.LocMemCache",
119
- "KEY_PREFIX": "", # This is important!
120
- "VERSION": None, # This is important!
121
- },
122
- }
123
- ```
124
- ### `forms.py`
125
-
126
- ```
127
- class YoursForm(TreeNodeForm):
128
- # Your code is here
129
- ```
130
-
131
-
132
- ## Usage
133
- ### Methods/Properties
134
-
135
- - [`delete`](#delete)
136
- - [`delete_tree`](#delete_tree)
137
- - [`get_ancestors`](#get_ancestors)
138
- - [`get_ancestors_count`](#get_ancestors_count)
139
- - [`get_ancestors_pks`](#get_ancestors_pks)
140
- - [`get_ancestors_queryset`](#get_ancestors_queryset)
141
- - [`get_breadcrumbs`](#get_breadcrumbs)
142
- - [`get_children`](#get_children)
143
- - [`get_children_count`](#get_children_count)
144
- - [`get_children_pks`](#get_children_pks)
145
- - [`get_children_queryset`](#get_children_queryset)
146
- - [`get_depth`](#get_depth)
147
- - [`get_descendants`](#get_descendants)
148
- - [`get_descendants_count`](#get_descendants_count)
149
- - [`get_descendants_pks`](#get_descendants_pks)
150
- - [`get_descendants_queryset`](#get_descendants_queryset)
151
- - [`get_descendants_tree`](#get_descendants_tree)
152
- - [`get_descendants_tree_display`](#get_descendants_tree_display)
153
- - [`get_first_child`](#get_first_child)
154
- - [`get_index`](#get_index)
155
- - [`get_last_child`](#get_last_child)
156
- - [`get_level`](#get_level)
157
- - [`get_order`](#get_order)
158
- - [`get_parent`](#get_parent)
159
- - [`get_parent_pk`](#get_parent_pk)
160
- - [`set_parent`](#set_parent)
161
- - [`get_path`](#get_path)
162
- - [`get_priority`](#get_priority)
163
- - [`set_priority`](#set_priority)
164
- - [`get_root`](#get_root)
165
- - [`get_root_pk`](#get_root_pk)
166
- - [`get_roots`](#get_roots)
167
- - [`get_roots_queryset`](#get_roots_queryset)
168
- - [`get_siblings`](#get_siblings)
169
- - [`get_siblings_count`](#get_siblings_count)
170
- - [`get_siblings_pks`](#get_siblings_pks)
171
- - [`get_siblings_queryset`](#get_siblings_queryset)
172
- - [`get_tree`](#get_tree)
173
- - [`get_tree_display`](#get_tree_display)
174
- - [`is_ancestor_of`](#is_ancestor_of)
175
- - [`is_child_of`](#is_child_of)
176
- - [`is_descendant_of`](#is_descendant_of)
177
- - [`is_first_child`](#is_first_child)
178
- - [`is_last_child`](#is_last_child)
179
- - [`is_leaf`](#is_leaf)
180
- - [`is_parent_of`](#is_parent_of)
181
- - [`is_root`](#is_root)
182
- - [`is_root_of`](#is_root_of)
183
- - [`is_sibling_of`](#is_sibling_of)
184
- - [`update_tree`](#update_tree)
185
-
186
- #### `delete`
187
- **Delete a node** provides two deletion strategies:
188
- - **Cascade Delete (`cascade=True`)**: Removes the node along with all its descendants.
189
- - **Reparenting (`cascade=False`)**: Moves the children of the deleted node up one level in the hierarchy before removing the node itself.
190
-
191
- ```python
192
- node.delete(cascade=True) # Deletes node and all its descendants
193
- node.delete(cascade=False) # Moves children up and then deletes the node
194
- ```
195
- This ensures greater flexibility in managing tree structures while preventing orphaned nodes.
196
-
197
- ---
198
-
199
- #### `delete_tree`
200
- **Delete the whole tree** for the current node class:
201
- ```python
202
- cls.delete_tree()
203
- ```
204
-
205
- #### `get_ancestors`
206
- Get a **list with all ancestors** (ordered from root to parent):
207
- ```python
208
- obj.get_ancestors(include_self=True, depth=None)
209
- # or
210
- obj.ancestors
211
- ```
212
-
213
- #### `get_ancestors_count`
214
- Get the **ancestors count**:
215
- ```python
216
- obj.get_ancestors_count(include_self=True, depth=None)
217
- # or
218
- obj.ancestors_count
219
- ```
220
-
221
- #### `get_ancestors_pks`
222
- Get the **ancestors pks** list:
223
- ```python
224
- obj.get_ancestors_pks(include_self=True, depth=None)
225
- # or
226
- obj.ancestors_pks
227
- ```
228
-
229
- #### `get_ancestors_queryset`
230
- Get the **ancestors queryset** (ordered from parent to root):
231
- ```python
232
- obj.get_ancestors_queryset(include_self=True, depth=None)
233
- ```
234
-
235
- #### `get_breadcrumbs`
236
- Get the **breadcrumbs** to current node (included):
237
- ```python
238
- obj.get_breadcrumbs(attr=None)
239
- # or
240
- obj.breadcrumbs
241
- ```
242
-
243
- #### `get_children`
244
- Get a **list containing all children**:
245
- ```python
246
- obj.get_children()
247
- # or
248
- obj.children
249
- ```
250
-
251
- #### `get_children_count`
252
- Get the **children count**:
253
- ```python
254
- obj.get_children_count()
255
- # or
256
- obj.children_count
257
- ```
258
-
259
- #### `get_children_pks`
260
- Get the **children pks** list:
261
- ```python
262
- obj.get_children_pks()
263
- # or
264
- obj.children_pks
265
- ```
266
-
267
- #### `get_children_queryset`
268
- Get the **children queryset**:
269
- ```python
270
- obj.get_children_queryset()
271
- ```
272
-
273
- #### `get_depth`
274
- Get the **node depth** (how many levels of descendants):
275
- ```python
276
- obj.get_depth()
277
- # or
278
- obj.depth
279
- ```
280
-
281
- #### `get_descendants`
282
- Get a **list containing all descendants**:
283
- ```python
284
- obj.get_descendants(include_self=False, depth=None)
285
- # or
286
- obj.descendants
287
- ```
288
-
289
- #### `get_descendants_count`
290
- Get the **descendants count**:
291
- ```python
292
- obj.get_descendants_count(include_self=False, depth=None)
293
- # or
294
- obj.descendants_count
295
- ```
296
-
297
- #### `get_descendants_pks`
298
- Get the **descendants pks** list:
299
- ```python
300
- obj.get_descendants_pks(include_self=False, depth=None)
301
- # or
302
- obj.descendants_pks
303
- ```
304
-
305
- #### `get_descendants_queryset`
306
- Get the **descendants queryset**:
307
- ```python
308
- obj.get_descendants_queryset(include_self=False, depth=None)
309
- ```
310
-
311
- #### `get_descendants_tree`
312
- Get a **n-dimensional** `dict` representing the **model tree**:
313
- ```python
314
- obj.get_descendants_tree()
315
- # or
316
- obj.descendants_tree
317
- ```
318
-
319
- **Important**: In future projects, avoid using `get_descendants_tree()`. It will be removed in the next version.
320
-
321
- #### `get_descendants_tree_display`
322
- Get a **multiline** `string` representing the **model tree**:
323
- ```python
324
- obj.get_descendants_tree_display(include_self=False, depth=None)
325
- # or
326
- obj.descendants_tree_display
327
- ```
328
-
329
- **Important**: In future projects, avoid using `get_descendants_tree_display()`. It will be removed in the next version.
330
-
331
- #### `get_first_child`
332
- Get the **first child node**:
333
- ```python
334
- obj.get_first_child()
335
- # or
336
- obj.first_child
337
- ```
338
-
339
- #### `get_index`
340
- Get the **node index** (index in node.parent.children list):
341
- ```python
342
- obj.get_index()
343
- # or
344
- obj.index
345
- ```
346
-
347
- #### `get_last_child`
348
- Get the **last child node**:
349
- ```python
350
- obj.get_last_child()
351
- # or
352
- obj.last_child
353
- ```
354
-
355
- #### `get_level`
356
- Get the **node level** (starting from 1):
357
- ```python
358
- obj.get_level()
359
- # or
360
- obj.level
361
- ```
362
-
363
- #### `get_order`
364
- Get the **order value** used for ordering:
365
- ```python
366
- obj.get_order()
367
- # or
368
- obj.order
369
- ```
370
-
371
- #### `get_parent`
372
- Get the **parent node**:
373
- ```python
374
- obj.get_parent()
375
- # or
376
- obj.parent
377
- ```
378
-
379
- #### `get_parent_pk`
380
- Get the **parent node pk**:
381
- ```python
382
- obj.get_parent_pk()
383
- # or
384
- obj.parent_pk
385
- ```
386
-
387
- #### `set_parent`
388
- Set the **parent node**:
389
- ```python
390
- obj.set_parent(parent_obj)
391
- ```
392
-
393
- #### `get_priority`
394
- Get the **node priority**:
395
- ```python
396
- obj.get_priority()
397
- # or
398
- obj.priority
399
- ```
400
- #### `get_path`
401
- Added the function of decorating a **materialized path**. The path is formed according to the value of the `tn_priority` field.
402
- ```python
403
- cls.get_path(prefix='', suffix='', delimiter='.', format_str='')
404
- ```
405
-
406
- #### `set_priority`
407
- Set the **node priority**:
408
- ```python
409
- obj.set_priority(100)
410
- ```
411
-
412
- #### `get_root`
413
- Get the **root node** for the current node:
414
- ```python
415
- obj.get_root()
416
- # or
417
- obj.root
418
- ```
419
-
420
- #### `get_root_pk`
421
- Get the **root node pk** for the current node:
422
- ```python
423
- obj.get_root_pk()
424
- # or
425
- obj.root_pk
426
- ```
427
-
428
- #### `get_roots`
429
- Get a **list with all root nodes**:
430
- ```python
431
- cls.get_roots()
432
- # or
433
- cls.roots
434
- ```
435
-
436
- #### `get_roots_queryset`
437
- Get **root nodes queryset**:
438
- ```python
439
- cls.get_roots_queryset()
440
- ```
441
-
442
- #### `get_siblings`
443
- Get a **list with all the siblings**:
444
- ```python
445
- obj.get_siblings()
446
- # or
447
- obj.siblings
448
- ```
449
-
450
- #### `get_siblings_count`
451
- Get the **siblings count**:
452
- ```python
453
- obj.get_siblings_count()
454
- # or
455
- obj.siblings_count
456
- ```
457
-
458
- #### `get_siblings_pks`
459
- Get the **siblings pks** list:
460
- ```python
461
- obj.get_siblings_pks()
462
- # or
463
- obj.siblings_pks
464
- ```
465
-
466
- #### `get_siblings_queryset`
467
- Get the **siblings queryset**:
468
- ```python
469
- obj.get_siblings_queryset()
470
- ```
471
-
472
- #### `get_tree`
473
- Returns an **n-dimensional dictionary** representing the model tree. Each node
474
- contains a "children"=[] key with a list of nested dictionaries of child nodes.:
475
- ```python
476
- cls.get_tree(instance=None)
477
- # or
478
- cls.tree
479
- ```
480
-
481
- #### `get_tree_display`
482
- Get a **multiline** `string` representing the **model tree**:
483
- ```python
484
- cls.get_tree_display()
485
- # or
486
- cls.tree_display
487
- ```
488
-
489
- #### `is_ancestor_of`
490
- Return `True` if the current node **is ancestor** of target_obj:
491
- ```python
492
- obj.is_ancestor_of(target_obj)
493
- ```
494
-
495
- #### `is_child_of`
496
- Return `True` if the current node **is child** of target_obj:
497
- ```python
498
- obj.is_child_of(target_obj)
499
- ```
500
-
501
- #### `is_descendant_of`
502
- Return `True` if the current node **is descendant** of target_obj:
503
- ```python
504
- obj.is_descendant_of(target_obj)
505
- ```
506
-
507
- #### `is_first_child`
508
- Return `True` if the current node is the **first child**:
509
- ```python
510
- obj.is_first_child()
511
- ```
512
-
513
- #### `is_last_child`
514
- Return `True` if the current node is the **last child**:
515
- ```python
516
- obj.is_last_child()
517
- ```
518
-
519
- #### `is_leaf`
520
- Return `True` if the current node is **leaf** (it has not children):
521
- ```python
522
- obj.is_leaf()
523
- ```
524
-
525
- #### `is_parent_of`
526
- Return `True` if the current node **is parent** of target_obj:
527
- ```python
528
- obj.is_parent_of(target_obj)
529
- ```
530
-
531
- #### `is_root`
532
- Return `True` if the current node **is root**:
533
- ```python
534
- obj.is_root()
535
- ```
536
-
537
- #### `is_root_of`
538
- Return `True` if the current node **is root** of target_obj:
539
- ```python
540
- obj.is_root_of(target_obj)
541
- ```
542
-
543
- #### `is_sibling_of`
544
- Return `True` if the current node **is sibling** of target_obj:
545
- ```python
546
- obj.is_sibling_of(target_obj)
547
- ```
548
-
549
- #### `update_tree`
550
- **Update tree** manually:
551
- ```python
552
- cls.update_tree()
553
- ```
554
- ## **Cache Management**
555
- ### **Overview**
556
- In v2.0, the caching mechanism has been improved to prevent excessive memory usage when multiple models inherit from `TreeNode`. The new system introduces **FIFO (First-In-First-Out) cache eviction**, with plans to test and integrate more advanced algorithms in future releases.
557
-
558
- ### **Key Features**
559
- **Global Cache Limit**: The setting `TREENODE_CACHE_LIMIT` defines the maximum cache size (in MB) for all models inheriting from `TreeNode`. Default is **100MB** if not explicitly set in `settings.py`.
560
- **settings.py**
561
- ``` python
562
- TREENODE_CACHE_LIMIT = 100
563
- ```
564
- **Automatic Management**. In most cases, users don’t need to manually manage cache operations.All methods that somehow change the state of models reset the tree cache automatically.
565
-
566
- **Manual Cache Clearing**. If for some reason you need to reset the cache, you can do it in two ways:
567
- - **Clear cache for a single model**: Use `clear_cache()` at the model level:
568
- ```python
569
- MyTreeNodeModel.clear_cache()
570
- ```
571
- - **Clear cache for all models**: Use the global `treenode_cache.clear()` method:
572
- ```python
573
- from treenode.cache import treenode_cache
574
- treenode_cache.clear()
575
- ```
576
-
577
- ## **Export and Import Functionality**
578
- ### **Overview**
579
- TreeNode v2.0 includes **built-in export and import features** for easier data migration. Supported Formats: `csv`, `json`, `xlsx`, `yaml`, `tsv`
580
- ### Installation for Import/Export Features
581
- By default, import/export functionality is **not included** to keep the package lightweight. If you need these features, install the package with:
582
- ```bash
583
- pip install django-fast-treenode[import_export]
584
- ```
585
- Once installed, **import/export buttons will appear** in the Django admin interface.
586
- ### **Important Considerations**
587
- Exporting objects with M2M fields may lead to serialization issues. Some formats (e.g., CSV) do not natively support many-to-many relationships. If you encounter errors, consider exporting data in `json` or `yaml` format, which better handle nested structures.
588
-
589
- ## Migration Guide
590
- #### Switching from `django-treenode`
591
- The migration process from `django-treenode` is fully automated. No manual steps are required. Upon upgrading, the necessary data structures will be checked and updated automatically. In exceptional cases, you can call the update code `cls.update_tree()` manually.
592
-
593
-
594
- #### Upgrading to `django-fast-treenode` 2.0
595
- To upgrade to version 2.0, simply run:
596
- ```bash
597
- pip install --upgrade django-fast-treenode
598
- ```
599
- or
600
- ```bash
601
- pip install django-fast-treenode[import_export]
602
- ```
603
- After upgrading, ensure that your database schema is up to date by running:
604
- ```bash
605
- python manage.py makemigrations
606
- python manage.py migrate
607
- ```
608
- This will apply any necessary database changes automatically.
609
-
610
-
611
- ## To do
612
- These improvements aim to enhance usability, performance, and maintainability for all users of `django-fast-treenode`:
613
- * **Cache Algorithm Optimization**: Testing and integrating more advanced cache eviction strategies.
614
- * **Drag-and-Drop UI Enhancements**: Adding intuitive drag-and-drop functionality for tree node management.
615
- * to be happy, to don't worry, until die.
616
-
617
- Your wishes, objections, comments are welcome.
618
-
619
-
620
- # Django-fast-treenode
621
-
622
- ## License
623
- Released under [MIT License](https://github.com/TimurKady/django-fast-treenode/blob/main/LICENSE).
624
-
625
- ## Cautions
626
- **Warning**: Do not access the tree node fields directly! Most of *django-treenode* model fields have been removed as unnecessary. Now only `tn_parent` and `tn_priority` are supported and will be kept in the future.
627
-
628
- **Risks of Direct Field Access:**
629
- - **Database Integrity Issues**: Directly modifying fields may break tree integrity, causing inconsistent parent-child relationships.
630
- - **Loss of Cached Data**: The caching system relies on controlled updates. Bypassing methods like `set_parent()` or `update_tree()` may lead to outdated or incorrect data.
631
- - **Unsupported Behavior**: Future versions may change field structures or remove unnecessary fields. Relying on them directly risks breaking compatibility.
632
-
633
- Instead, always use the **documented methods** described above or refer to the [original application documentation](https://github.com/fabiocaccamo/django-treenode).
634
-
635
- ## Credits
636
- This software contains, uses, and includes, in a modified form, [django-treenode](https://github.com/fabiocaccamo/django-treenode) by [Fabio Caccamo](https://github.com/fabiocaccamo). Special thanks to [Mathieu Leplatre](https://blog.mathieu-leplatre.info/pages/about.html) for the advice used in writing this application.