Projet

Général

Profil

0001-formdefs-store-reverse-relations-on-form-carddefs-57.patch

Lauréline Guérin, 14 novembre 2021 19:34

Télécharger (12 ko)

Voir les différences:

Subject: [PATCH] formdefs: store reverse relations on form/carddefs (#57963)

 tests/test_carddef.py | 182 +++++++++++++++++++++++++++++++++++++++++-
 wcs/formdef.py        |  79 ++++++++++++++++++
 2 files changed, 260 insertions(+), 1 deletion(-)
tests/test_carddef.py
3 3

  
4 4
import pytest
5 5

  
6
from wcs.blocks import BlockDef
6 7
from wcs.carddef import CardDef
7 8
from wcs.categories import CardDefCategory
8
from wcs.fields import ItemField, StringField
9
from wcs.fields import BlockField, ItemField, ItemsField, StringField
10
from wcs.formdef import FormDef
9 11
from wcs.qommon.http_request import HTTPRequest
10 12
from wcs.qommon.misc import indent_xml as indent
11 13
from wcs.qommon.template import Template
......
583 585
    cards = CardDef.get_data_source_items('carddef:foo', query='_')
584 586
    assert len(cards) == 1
585 587
    assert cards[0]['text'] == 'Astreinte\\Mardi _'
588

  
589

  
590
def test_reverse_relations(pub):
591
    FormDef.wipe()
592
    CardDef.wipe()
593
    BlockDef.wipe()
594

  
595
    formdef1 = FormDef()
596
    formdef1.name = 'formdef 1'
597
    formdef1.store()
598

  
599
    formdef2 = FormDef()
600
    formdef2.name = 'formdef 2'
601
    formdef2.store()
602

  
603
    carddef1 = CardDef()
604
    carddef1.name = 'carddef 1'
605
    carddef1.store()
606

  
607
    carddef2 = CardDef()
608
    carddef2.name = 'carddef 2'
609
    carddef2.store()
610

  
611
    block1 = BlockDef()
612
    block1.name = 'block 1'
613
    block1.fields = [
614
        ItemField(
615
            id='1',
616
            label='item',
617
            type='item',
618
            varname='block_foo_1',
619
            data_source={'type': 'carddef:carddef-1'},
620
        ),
621
        ItemsField(id='2', label='items', type='items', data_source={'type': 'carddef:carddef-1'}),
622
    ]
623
    block1.store()
624

  
625
    assert formdef1.reverse_relations == []
626
    assert formdef2.reverse_relations == []
627
    assert carddef1.reverse_relations == []
628
    assert carddef2.reverse_relations == []
629

  
630
    formdef1.fields = [
631
        ItemField(id='1', label='item', type='item', data_source={'type': 'carddef:carddef-1'}),
632
    ]
633
    formdef1.store()
634

  
635
    formdef1.refresh_from_storage()
636
    formdef2.refresh_from_storage()
637
    carddef1.refresh_from_storage()
638
    carddef2.refresh_from_storage()
639
    assert formdef1.reverse_relations == []
640
    assert formdef2.reverse_relations == []
641
    assert carddef1.reverse_relations == [
642
        {'varname': '', 'type': 'item', 'obj': 'formdef:formdef-1'},
643
    ]
644
    assert carddef2.reverse_relations == []
645

  
646
    formdef2.fields = [
647
        ItemsField(
648
            id='1', label='items', type='items', varname='bar', data_source={'type': 'carddef:carddef-2'}
649
        ),
650
    ]
651
    formdef2.store()
652

  
653
    formdef1.refresh_from_storage()
654
    formdef2.refresh_from_storage()
655
    carddef1.refresh_from_storage()
656
    carddef2.refresh_from_storage()
657
    assert formdef1.reverse_relations == []
658
    assert formdef2.reverse_relations == []
659
    assert carddef1.reverse_relations == [
660
        {'varname': '', 'type': 'item', 'obj': 'formdef:formdef-1'},
661
    ]
662
    assert carddef2.reverse_relations == [
663
        {'varname': 'bar', 'type': 'items', 'obj': 'formdef:formdef-2'},
664
    ]
665

  
666
    carddef1.fields = [
667
        ItemField(id='1', label='item', type='item', data_source={'type': 'carddef:carddef-2'}),
668
    ]
669
    carddef1.store()
670

  
671
    formdef1.refresh_from_storage()
672
    formdef2.refresh_from_storage()
673
    carddef1.refresh_from_storage()
674
    carddef2.refresh_from_storage()
675
    assert formdef1.reverse_relations == []
676
    assert formdef2.reverse_relations == []
677
    assert carddef1.reverse_relations == [
678
        {'varname': '', 'type': 'item', 'obj': 'formdef:formdef-1'},
679
    ]
680
    assert carddef2.reverse_relations == [
681
        {'varname': '', 'type': 'item', 'obj': 'carddef:carddef-1'},
682
        {'varname': 'bar', 'type': 'items', 'obj': 'formdef:formdef-2'},
683
    ]
684

  
685
    carddef1.fields = [
686
        ItemsField(id='1', label='items', type='items', data_source={'type': 'carddef:carddef-2'}),
687
    ]
688
    carddef1.store()
689

  
690
    formdef1.refresh_from_storage()
691
    formdef2.refresh_from_storage()
692
    carddef1.refresh_from_storage()
693
    carddef2.refresh_from_storage()
694
    assert formdef1.reverse_relations == []
695
    assert formdef2.reverse_relations == []
696
    assert carddef1.reverse_relations == [
697
        {'varname': '', 'type': 'item', 'obj': 'formdef:formdef-1'},
698
    ]
699
    assert carddef2.reverse_relations == [
700
        {'varname': '', 'type': 'items', 'obj': 'carddef:carddef-1'},
701
        {'varname': 'bar', 'type': 'items', 'obj': 'formdef:formdef-2'},
702
    ]
703

  
704
    # circular relation ?
705
    carddef2.fields = [
706
        ItemsField(id='1', label='items', type='items', data_source={'type': 'carddef:carddef-2'}),
707
    ]
708
    carddef2.store()
709

  
710
    formdef1.refresh_from_storage()
711
    formdef2.refresh_from_storage()
712
    carddef1.refresh_from_storage()
713
    carddef2.refresh_from_storage()
714
    assert formdef1.reverse_relations == []
715
    assert formdef2.reverse_relations == []
716
    assert carddef1.reverse_relations == [
717
        {'varname': '', 'type': 'item', 'obj': 'formdef:formdef-1'},
718
    ]
719
    assert carddef2.reverse_relations == [
720
        {'varname': '', 'type': 'items', 'obj': 'carddef:carddef-1'},
721
        {'varname': '', 'type': 'items', 'obj': 'carddef:carddef-2'},
722
        {'varname': 'bar', 'type': 'items', 'obj': 'formdef:formdef-2'},
723
    ]
724

  
725
    # block field
726
    formdef1.fields.append(BlockField(id='2', label='block', type='block:%s' % block1.slug))
727
    formdef1.store()
728

  
729
    formdef1.refresh_from_storage()
730
    formdef2.refresh_from_storage()
731
    carddef1.refresh_from_storage()
732
    carddef2.refresh_from_storage()
733
    assert formdef1.reverse_relations == []
734
    assert formdef2.reverse_relations == []
735
    # no varname for block field, item/formdef-1 is already in reverse_relations
736
    assert carddef1.reverse_relations == [
737
        {'varname': '', 'type': 'item', 'obj': 'formdef:formdef-1'},
738
        {'varname': '', 'type': 'items', 'obj': 'formdef:formdef-1'},
739
    ]
740
    assert carddef2.reverse_relations == [
741
        {'varname': '', 'type': 'items', 'obj': 'carddef:carddef-1'},
742
        {'varname': '', 'type': 'items', 'obj': 'carddef:carddef-2'},
743
        {'varname': 'bar', 'type': 'items', 'obj': 'formdef:formdef-2'},
744
    ]
745

  
746
    formdef1.fields[1] = BlockField(id='2', label='block', type='block:%s' % block1.slug, varname='foo')
747
    formdef1.store()
748

  
749
    formdef1.refresh_from_storage()
750
    formdef2.refresh_from_storage()
751
    carddef1.refresh_from_storage()
752
    carddef2.refresh_from_storage()
753
    assert formdef1.reverse_relations == []
754
    assert formdef2.reverse_relations == []
755
    # varname defined for block field
756
    assert carddef1.reverse_relations == [
757
        {'varname': '', 'type': 'item', 'obj': 'formdef:formdef-1'},
758
        {'varname': '', 'type': 'items', 'obj': 'formdef:formdef-1'},
759
        {'varname': 'foo_block_foo_1', 'type': 'item', 'obj': 'formdef:formdef-1'},
760
    ]
761
    assert carddef2.reverse_relations == [
762
        {'varname': '', 'type': 'items', 'obj': 'carddef:carddef-1'},
763
        {'varname': '', 'type': 'items', 'obj': 'carddef:carddef-2'},
764
        {'varname': 'bar', 'type': 'items', 'obj': 'formdef:formdef-2'},
765
    ]
wcs/formdef.py
28 28
import time
29 29
import types
30 30
import xml.etree.ElementTree as ET
31
from operator import itemgetter
31 32

  
32 33
from django.utils.encoding import force_bytes, force_text
33 34
from quixote import get_publisher, get_session
......
145 146

  
146 147
    max_field_id = None
147 148

  
149
    # store reverse relations
150
    reverse_relations = None
151

  
148 152
    # store fields in a separate pickle chunk
149 153
    lightweight = True
150 154

  
......
445 449
            if self.id is None or get_publisher().is_using_postgresql() or self.data_class().count() == 0:
446 450
                self.internal_identifier = new_internal_identifier
447 451
        object_only = kwargs.pop('object_only', False)
452

  
453
        if not object_only:
454
            self.update_relations()
455

  
448 456
        StorableObject.store(self, *args, **kwargs)
457

  
449 458
        if object_only:
450 459
            return
451 460
        if get_publisher().snapshot_class:
......
466 475
            for action in actions:
467 476
                getattr(cls, action)()
468 477

  
478
    def update_relations(self):
479
        from wcs.carddef import CardDef
480

  
481
        self_ref = '%s:%s' % (self.xml_root_node, self.url_name)
482
        self_relations_by_ref = self.build_relations_by_ref()
483
        reverse_relations = []
484

  
485
        # cross each formdef and cardef and check relations
486
        for objdef in itertools.chain(
487
            FormDef.select(ignore_errors=True, ignore_migration=True),
488
            CardDef.select(ignore_errors=True, ignore_migration=True),
489
        ):
490
            objdef_ref = '%s:%s' % (objdef.xml_root_node, objdef.url_name)
491
            if objdef.xml_root_node == self.xml_root_node and objdef.id == self.id:
492
                # don't build relations twice
493
                objdef_relations_by_ref = self_relations_by_ref
494
            else:
495
                objdef_relations_by_ref = objdef.build_relations_by_ref()
496
            reverse_relations += objdef_relations_by_ref.get(self_ref, [])
497

  
498
            # remove relations with self in objdef's reverse_relations
499
            objdef.reverse_relations = [r for r in (objdef.reverse_relations or []) if r['obj'] != self_ref]
500
            # and update objdef's reverse_relations from self_relations_by_ref
501
            objdef.reverse_relations += self_relations_by_ref.get(objdef_ref, [])
502
            # sort objectdef's reverse_relations
503
            objdef.reverse_relations = sorted(
504
                objdef.reverse_relations, key=itemgetter('obj', 'varname', 'type')
505
            )
506
            objdef.store(object_only=True)
507
        # sort self's reverse_relations and set
508
        self.reverse_relations = sorted(reverse_relations, key=itemgetter('obj', 'varname', 'type'))
509

  
510
    def build_relations_by_ref(self):
511
        # build relations to other carddefs, to be stored in some object reverse field
512
        self_ref = '%s:%s' % (self.xml_root_node, self.url_name)
513
        relations_by_ref = collections.defaultdict(list)
514

  
515
        def _check_field(field, in_block_field=False, prefix=''):
516
            data_source = getattr(field, 'data_source', None)
517
            if not data_source or not data_source['type'].startswith('carddef:'):
518
                return
519
            varname = field.varname or ''
520
            if in_block_field:
521
                if prefix and field.varname:
522
                    varname = '%s_%s' % (prefix, field.varname)
523
                else:
524
                    varname = ''
525
            # reverse relation of data_source['type'] to this object
526
            relations_by_ref[data_source['type']].append(
527
                {
528
                    'varname': varname,
529
                    'type': field.key,
530
                    'obj': self_ref,
531
                }
532
            )
533

  
534
        for field in self.get_all_fields():
535
            if field.key in ['item', 'items']:
536
                _check_field(field)
537
            if field.key == 'block':
538
                for _field in field.block.fields:
539
                    if _field.key not in ['item', 'items']:
540
                        continue
541
                    _check_field(_field, in_block_field=True, prefix=field.varname or '')
542

  
543
        # remove duplicated items
544
        return {
545
            k: list(map(dict, {tuple(sorted(d.items())) for d in v})) for k, v in relations_by_ref.items()
546
        }
547

  
469 548
    def store_related_custom_views(self):
470 549
        for view in getattr(self, '_custom_views', []):
471 550
            view.formdef = self
472
-