|
5 | 5 | query. These queries also topologically sort the ids by generation.
|
6 | 6 | """
|
7 | 7 |
|
| 8 | +from copy import deepcopy |
8 | 9 | from django.apps import apps
|
9 | 10 | from django.db import models, connection
|
10 | 11 | from django.db.models import Case, When
|
@@ -467,6 +468,108 @@ def sort(self, edges, **kwargs):
|
467 | 468 | # ToDo: Implement
|
468 | 469 | pass
|
469 | 470 |
|
| 471 | + def insert_node(self, edge, node, clone_to_rootside=False, clone_to_leafside=False, pre_save=None, post_save=None): |
| 472 | + """ |
| 473 | + Inserts a node into an existing Edge instance. Returns a tuple of the newly created rootside_edge (parent to |
| 474 | + the inserted node) and leafside_edge (child to the inserted node). |
| 475 | +
|
| 476 | + Process: |
| 477 | + 1. Add a new Edge from the parent Node of the current Edge instance to the provided Node instance, |
| 478 | + optionally cloning properties of the existing Edge. |
| 479 | + 2. Add a new Edge from the provided Node instance to the child Node of the current Edge instance, |
| 480 | + optionally cloning properties of the existing Edge. |
| 481 | + 3. Remove the original Edge instance. |
| 482 | +
|
| 483 | + The instance will still exist in memory, though not in database |
| 484 | + (https://docs.djangoproject.com/en/3.1/ref/models/instances/#refreshing-objects-from-database). |
| 485 | + Recommend running the following after conducting the deletion: |
| 486 | + `del instancename` |
| 487 | +
|
| 488 | + Cloning will fail if a field has unique=True, so a pre_save function can be passed into this method |
| 489 | + Likewise, a post_save function can be passed in to rebuild relationships. For instance, if you have a `name` |
| 490 | + field that is unique and generated automatically in the model's save() method, you could pass in a the following |
| 491 | + `pre_save` function to clear the name prior to saving the new Edge instance(s): |
| 492 | +
|
| 493 | + def pre_save(new_edge): |
| 494 | + new_edge.name = "" |
| 495 | + return new_edge |
| 496 | +
|
| 497 | + A more complete example, where we have models named NetworkEdge & NetworkNode, and we want to insert a new |
| 498 | + Node (n2) into Edge e1, while copying e1's field properties (except `name`) to the newly created rootside Edge |
| 499 | + instance (n1 to n2) is shown below. |
| 500 | +
|
| 501 | + Original Final |
| 502 | +
|
| 503 | + n1 o n1 o |
| 504 | + | \ |
| 505 | + | o n2 |
| 506 | + | / |
| 507 | + n3 o n3 o |
| 508 | +
|
| 509 | + ################################################################################## |
| 510 | + from myapp.models import NetworkEdge, NetworkNode |
| 511 | +
|
| 512 | + n1 = NetworkNode.objects.create(name="n1") |
| 513 | + n2 = NetworkNode.objects.create(name="n2") |
| 514 | + n3 = NetworkNode.objects.create(name="n3") |
| 515 | +
|
| 516 | + # Connect n3 to n1 |
| 517 | + n1.add_child(n3) |
| 518 | +
|
| 519 | + e1 = NetworkEdge.objects.last() |
| 520 | +
|
| 521 | + # function to clear the `name` field, which is autogenerated and must be unique |
| 522 | + def pre_save(new_edge): |
| 523 | + new_edge.name = "" |
| 524 | + return new_edge |
| 525 | +
|
| 526 | + NetworkEdge.objects.insert_node(e1, n2, clone_to_rootside=True, pre_save=pre_save) |
| 527 | + ################################################################################## |
| 528 | + """ |
| 529 | + |
| 530 | + rootside_edge = None |
| 531 | + leafside_edge = None |
| 532 | + |
| 533 | + # Attach the root-side edge |
| 534 | + if clone_to_rootside: |
| 535 | + rootside_edge = deepcopy(edge) |
| 536 | + rootside_edge.pk = None |
| 537 | + rootside_edge.parent = edge.parent |
| 538 | + rootside_edge.child = node |
| 539 | + |
| 540 | + if callable(pre_save): |
| 541 | + rootside_edge = pre_save(rootside_edge) |
| 542 | + |
| 543 | + rootside_edge.save() |
| 544 | + |
| 545 | + if callable(post_save): |
| 546 | + rootside_edge = post_save(rootside_edge) |
| 547 | + |
| 548 | + else: |
| 549 | + edge.parent.add_child(node) |
| 550 | + |
| 551 | + # Attach the leaf-side edge |
| 552 | + if clone_to_leafside: |
| 553 | + leafside_edge = deepcopy(edge) |
| 554 | + leafside_edge.pk = None |
| 555 | + leafside_edge.parent = node |
| 556 | + leafside_edge.child = edge.child |
| 557 | + |
| 558 | + if callable(pre_save): |
| 559 | + leafside_edge = pre_save(leafside_edge) |
| 560 | + |
| 561 | + leafside_edge.save() |
| 562 | + |
| 563 | + if callable(post_save): |
| 564 | + leafside_edge = post_save(leafside_edge) |
| 565 | + |
| 566 | + else: |
| 567 | + edge.child.add_parent(node) |
| 568 | + |
| 569 | + # Remove the original edge in the database. Still remains in memory, though, as noted above. |
| 570 | + edge.delete() |
| 571 | + return rootside_edge, leafside_edge |
| 572 | + |
470 | 573 |
|
471 | 574 | def edge_factory(
|
472 | 575 | node_model,
|
|
0 commit comments