Stop using unique identifier on archive unit and data objects
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Tue, 10 Jan 2017 17:51:45 +0100
changeset 2264 5fb5bf38f962
parent 2263 b6a2ad4c7683
child 2265 aac19cc4a92e
Stop using unique identifier on archive unit and data objects They have been introduced at a first glance to handle references during profile generation as well as display of 'first class' element in the UI. The main problem is that they clash when using the 'import archive unit' feature, since one may not import the same unit twice without changing the id first. Another problem is that their purpose is not clear to end-user. This changeset remove the attribute, use eid instead during profile generation and fix tests. UI and proper migration will be handled in later csets. Related #16070476
entities/generated.py
entities/itree.py
entities/profile_generation.py
i18n/en.po
i18n/fr.po
migration/0.6.0_Any.py
schema/seda2.py
test/test_diag.py
test/test_entities.py
test/test_hooks.py
test/test_html_generation.py
test/test_profile_generation.py
test/test_schema.py
test/test_views.py
test/testutils.py
views/uicfg.py
xsd2entities.py
xsd2uicfg.py
xsd2yams.py
--- a/entities/generated.py	Tue Jan 10 16:07:35 2017 +0100
+++ b/entities/generated.py	Tue Jan 10 17:51:45 2017 +0100
@@ -115,13 +115,13 @@
 
 class SEDABinaryDataObject(SEDAAnyEntity):
     __regid__ = 'SEDABinaryDataObject'
-    fetch_attrs, cw_fetch_order = fetch_config(['id', 'filename', 'user_cardinality', 'user_annotation'])
-    value_attr = 'id'
+    fetch_attrs, cw_fetch_order = fetch_config(['filename', 'user_cardinality', 'user_annotation'])
+    value_attr = None
 
 class SEDAPhysicalDataObject(SEDAAnyEntity):
     __regid__ = 'SEDAPhysicalDataObject'
-    fetch_attrs, cw_fetch_order = fetch_config(['id', 'user_cardinality', 'user_annotation'])
-    value_attr = 'id'
+    fetch_attrs, cw_fetch_order = fetch_config(['user_cardinality', 'user_annotation'])
+    value_attr = None
 
 class SEDARelationship(SEDAAnyEntity):
     __regid__ = 'SEDARelationship'
@@ -145,8 +145,8 @@
 
 class SEDAArchiveUnit(SEDAAnyEntity):
     __regid__ = 'SEDAArchiveUnit'
-    fetch_attrs, cw_fetch_order = fetch_config(['id', 'user_cardinality', 'user_annotation'])
-    value_attr = 'id'
+    fetch_attrs, cw_fetch_order = fetch_config(['user_cardinality', 'user_annotation'])
+    value_attr = None
 
 class SEDAServiceLevel(SEDAAnyEntity):
     __regid__ = 'SEDAServiceLevel'
--- a/entities/itree.py	Tue Jan 10 16:07:35 2017 +0100
+++ b/entities/itree.py	Tue Jan 10 17:51:45 2017 +0100
@@ -109,7 +109,7 @@
         seq = self.entity.first_level_choice.content_sequence
         assert seq is not None, self.entity  # can't be None in simplified profile
         for do in self._cw.execute(
-                'Any DO, DOID ORDERBY DOID WHERE DO id DOID, '
+                'Any DO, DOUA ORDERBY DOUA WHERE DO user_annotation DOUA, '
                 'REF seda_data_object_reference_id DO, '
                 'REF seda_data_object_reference SEQ, SEQ eid %(x)s',
                 {'x': seq.eid}).entities():
--- a/entities/profile_generation.py	Tue Jan 10 16:07:35 2017 +0100
+++ b/entities/profile_generation.py	Tue Jan 10 17:51:45 2017 +0100
@@ -72,6 +72,12 @@
     return content_types
 
 
+def _internal_reference(value):
+    """Return True if the given value is a reference to an entity within the profile."""
+    return getattr(value, 'cw_etype', None) in ('SEDAArchiveUnit',
+                                                'SEDABinaryDataObject', 'SEDAPhysicalDataObject')
+
+
 def _concept_value(concept, language):
     """Return string value to be inserted in a SEDA export for the given concept.
 
@@ -92,6 +98,11 @@
                        % (concept.eid, language))
 
 
+def eid2xmlid(eid):
+    """Return a value usable as ID/IDREF for the given eid."""
+    return 'id' + text_type(eid)
+
+
 def serialize(value):
     """Return typed `value` as an XSD string."""
     if value is None:
@@ -101,9 +112,8 @@
             return value.absolute_url()
         if value.cw_etype == 'Concept':
             return _concept_value(value, 'seda-2')
-        if hasattr(value, 'id'):
-            # value is something in the profile which has a id (e.g. archive unit, data object)
-            return value.id
+        if _internal_reference(value):
+            return eid2xmlid(value.eid)
         return None  # intermediary entity
     if isinstance(value, bool):
         return 'true' if value else 'false'
@@ -508,7 +518,7 @@
                     self.element('xsd:attribute', attributes=attrs, parent=extension_element)
         fixed_value = serialize(value)
         if fixed_value is not None:
-            attr = 'default' if hasattr(value, 'id') else 'fixed'
+            attr = 'default' if _internal_reference(value) else 'fixed'
             target_element.attrib[attr] = fixed_value
 
     def concept_scheme_attribute(self, xselement, type_element, scheme):
@@ -692,7 +702,7 @@
                     self._rng_attribute(occ.target, parent_element)
         fixed_value = serialize(value)
         if fixed_value is not None:
-            if hasattr(value, 'id'):
+            if _internal_reference(value):
                 profile_element.attrib[self.qname('a:defaultValue')] = fixed_value
                 self.element('rng:data', profile_element, {'type': 'NCName'})
             else:
@@ -1510,6 +1520,8 @@
 
 def _simple_path_target_values(entity, rtype, role, target_etype):
     if target_etype in BASE_TYPES:
+        if rtype == 'id':
+            return [(None, eid2xmlid(entity.eid))]
         return [(None, getattr(entity, rtype, None))]
     targets = entity.related(rtype, role, entities=True)
     rschema = entity._cw.vreg.schema.rschema
--- a/i18n/en.po	Tue Jan 10 16:07:35 2017 +0100
+++ b/i18n/en.po	Tue Jan 10 17:51:45 2017 +0100
@@ -100,11 +100,6 @@
 "considered."
 msgstr ""
 
-msgid ""
-"Default identifier that will be used to handle references. This is not "
-"necessarily the identifier that will appear in the final sheet."
-msgstr ""
-
 msgid "Entity"
 msgstr ""
 
@@ -4610,24 +4605,6 @@
 msgid "format_litteral"
 msgstr ""
 
-msgid "id"
-msgstr ""
-
-msgctxt "SEDAArchiveUnit"
-msgid "id"
-msgstr ""
-
-msgctxt "SEDABinaryDataObject"
-msgid "id"
-msgstr ""
-
-msgctxt "SEDAPhysicalDataObject"
-msgid "id"
-msgstr ""
-
-msgid "identifier must be unique within a profile"
-msgstr ""
-
 msgid "import menu"
 msgstr "import"
 
--- a/i18n/fr.po	Tue Jan 10 16:07:35 2017 +0100
+++ b/i18n/fr.po	Tue Jan 10 17:51:45 2017 +0100
@@ -104,14 +104,6 @@
 "L'historique de conservation est du texte en SEDA 0.2, un seul élément est "
 "autorisé."
 
-msgid ""
-"Default identifier that will be used to handle references. This is not "
-"necessarily the identifier that will appear in the final sheet."
-msgstr ""
-"Identifiant qui sera utilisé pour gérer les références à la génération du "
-"profil - ce n'est pas nécessairement l'identifiant qui sera utilisé dans les "
-"bordereaux de transfert"
-
 msgid "Entity"
 msgstr "Entité"
 
@@ -4632,24 +4624,6 @@
 msgid "format_litteral"
 msgstr ""
 
-msgid "id"
-msgstr "identifiant"
-
-msgctxt "SEDAArchiveUnit"
-msgid "id"
-msgstr ""
-
-msgctxt "SEDABinaryDataObject"
-msgid "id"
-msgstr ""
-
-msgctxt "SEDAPhysicalDataObject"
-msgid "id"
-msgstr ""
-
-msgid "identifier must be unique within a profile"
-msgstr "un identifiant doit être unique au sein d'un profil"
-
 msgid "import menu"
 msgstr "importer"
 
--- a/migration/0.6.0_Any.py	Tue Jan 10 16:07:35 2017 +0100
+++ b/migration/0.6.0_Any.py	Tue Jan 10 17:51:45 2017 +0100
@@ -10,3 +10,6 @@
 commit()
 
 sync_schema_props_perms(('SEDAArchiveTransfer', 'title', 'String'))
+
+for etype in ('SEDAArchiveUnit', 'SEDABinaryDataObject', 'SEDAPhysicalDataObject'):
+    drop_attribute(etype, 'id')
--- a/schema/seda2.py	Tue Jan 10 16:07:35 2017 +0100
+++ b/schema/seda2.py	Tue Jan 10 17:51:45 2017 +0100
@@ -424,7 +424,6 @@
 class SEDABinaryDataObject(EntityType):
     u""""""
     filename = String(fulltextindexed=True)
-    id = String(required=True, description=_('Default identifier that will be used to handle references. This is not necessarily the identifier that will appear in the final sheet.'), constraints=[RQLConstraint('NOT EXISTS(S id I, X id I, S container C, X container C, NOT S identity X)', msg=_('identifier must be unique within a profile'))])
 
 
 class archive_transfer_binary_data_object(RelationDefinition):
@@ -440,7 +439,6 @@
                       annotable=True)
 class SEDAPhysicalDataObject(EntityType):
     u""""""
-    id = String(required=True, description=_('Default identifier that will be used to handle references. This is not necessarily the identifier that will appear in the final sheet.'), constraints=[RQLConstraint('NOT EXISTS(S id I, X id I, S container C, X container C, NOT S identity X)', msg=_('identifier must be unique within a profile'))])
 
 
 class archive_transfer_physical_data_object(RelationDefinition):
@@ -535,7 +533,6 @@
                       annotable=True)
 class SEDAArchiveUnit(EntityType):
     u""""""
-    id = String(required=True, description=_('Default identifier that will be used to handle references. This is not necessarily the identifier that will appear in the final sheet.'), constraints=[RQLConstraint('NOT EXISTS(S id I, X id I, S container C, X container C, NOT S identity X)', msg=_('identifier must be unique within a profile'))])
 
 
 class archive_transfer_archive_unit(RelationDefinition):
--- a/test/test_diag.py	Tue Jan 10 16:07:35 2017 +0100
+++ b/test/test_diag.py	Tue Jan 10 17:51:45 2017 +0100
@@ -127,7 +127,7 @@
         with self.admin_access.repo_cnx() as cnx:
             transfer = cnx.create_entity('SEDAArchiveTransfer', title=u'diagnosis testing')
             unit, unit_alt, unit_alt_seq = create_archive_unit(transfer)
-            create_archive_unit(unit_alt_seq, archive_unit_reference=True, id=u'auref')
+            create_archive_unit(unit_alt_seq, archive_unit_reference=True)
 
             doctor = transfer.cw_adapt_to('ISEDACompatAnalyzer')
             cnx.commit()
--- a/test/test_entities.py	Tue Jan 10 16:07:35 2017 +0100
+++ b/test/test_entities.py	Tue Jan 10 17:51:45 2017 +0100
@@ -84,11 +84,10 @@
             self.assertEqual(bdo.container[0].eid, unit.eid)
 
             # test clone without reparenting
-            cloned = cnx.create_entity(unit.cw_etype, id=u'hop', user_annotation=u'clone',
+            cloned = cnx.create_entity(unit.cw_etype, user_annotation=u'clone',
                                        clone_of=unit)
             cnx.commit()
             cloned.cw_clear_all_caches()
-            self.assertEqual(cloned.id, 'hop')
             self.assertEqual(cloned.user_annotation, 'clone')
             cloned_unit_alt_seq = cloned.first_level_choice.content_sequence
             self.assertEqual(cloned_unit_alt_seq.container[0].eid, cloned.eid)
@@ -98,7 +97,7 @@
 
             # test clone with reparenting
             transfer = cnx.create_entity('SEDAArchiveTransfer', title=u'test profile')
-            cloned = cnx.create_entity(unit.cw_etype, id=u'hop',
+            cloned = cnx.create_entity(unit.cw_etype,
                                        clone_of=unit,
                                        seda_archive_unit=transfer)
             cnx.commit()
--- a/test/test_hooks.py	Tue Jan 10 16:07:35 2017 +0100
+++ b/test/test_hooks.py	Tue Jan 10 17:51:45 2017 +0100
@@ -98,7 +98,7 @@
             cnx.create_entity('SEDATitle', seda_title=content)
             cnx.commit()
             # Check that everything goes fine when the hook is called
-            cnx.execute('DELETE SEDAArchiveUnit X WHERE X id "au1"')
+            au.cw_delete()
             cnx.commit()
 
 
--- a/test/test_html_generation.py	Tue Jan 10 16:07:35 2017 +0100
+++ b/test/test_html_generation.py	Tue Jan 10 17:51:45 2017 +0100
@@ -90,8 +90,8 @@
                 AttrDef(label='algorithm', card='mandatory', value=None),
                 AttrDef(label='filename', card='optional', value=None),
                 AttrDef(label='href', card='optional', value=None),
-                AttrDef(label='id', card='mandatory', value='au1'),
-                AttrDef(label='id', card='mandatory', value='bdo1'),
+                AttrDef(label='id', card='mandatory', value='id{}'.format(self.au_eid)),
+                AttrDef(label='id', card='mandatory', value='id{}'.format(self.bdo_eid)),
                 AttrDef(label='id', card='optional', value=None),
                 AttrDef(label='when', card='optional', value=None),
                 AttrDef(label='uri', card='optional', value=None),
--- a/test/test_profile_generation.py	Tue Jan 10 16:07:35 2017 +0100
+++ b/test/test_profile_generation.py	Tue Jan 10 17:51:45 2017 +0100
@@ -34,7 +34,7 @@
 from cubicweb.devtools.testlib import CubicWebTC
 
 from cubes.seda.xsd2yams import XSDMMapping
-from cubes.seda.entities.profile_generation import _path_target_values
+from cubes.seda.entities.profile_generation import _path_target_values, eid2xmlid
 
 import testutils
 
@@ -362,7 +362,7 @@
     def test_skipped_mandatory_complex(self):
         with self.admin_access.client_cnx() as cnx:
             transfer = cnx.create_entity('SEDAArchiveTransfer', title=u'test profile')
-            testutils.create_data_object(transfer, id=u"bdo1", filename=u'fixed.txt')
+            testutils.create_data_object(transfer, filename=u'fixed.txt')
             profile = self.profile_etree(transfer)
             fname = self.get_element(profile, 'Filename')
             self.assertElementDefinition(fname, {'name': 'Filename',
@@ -381,7 +381,7 @@
     def test_fileinfo_card(self):
         with self.admin_access.client_cnx() as cnx:
             transfer = cnx.create_entity('SEDAArchiveTransfer', title=u'test profile')
-            bdo = cnx.create_entity('SEDABinaryDataObject', id=u"bdo1",
+            bdo = cnx.create_entity('SEDABinaryDataObject',
                                     seda_binary_data_object=transfer)
             appname = cnx.create_entity('SEDACreatingApplicationName',
                                         seda_creating_application_name=bdo)
@@ -404,7 +404,7 @@
     def test_data_object_package_card(self):
         with self.admin_access.client_cnx() as cnx:
             transfer = cnx.create_entity('SEDAArchiveTransfer', title=u'test profile')
-            bdo = cnx.create_entity('SEDABinaryDataObject', id=u"bdo1",
+            bdo = cnx.create_entity('SEDABinaryDataObject',
                               seda_binary_data_object=transfer)
 
             profile = self.profile_etree(transfer)
@@ -535,7 +535,7 @@
             some_concept = scheme.add_concept(label=u'md5 algorithm', language_code=u'en')
             transfer = create('SEDAArchiveTransfer', title=u'test profile',
                               seda_message_digest_algorithm_code_list_version=scheme)
-            create('SEDABinaryDataObject', id=u"bdo1", user_cardinality=u'0..n',
+            create('SEDABinaryDataObject', user_cardinality=u'0..n',
                    seda_binary_data_object=transfer,
                    seda_algorithm=some_concept)
 
@@ -615,7 +615,7 @@
                                      reverse_seda_seq_access_rule_rule=access_rule)
             create('SEDAStartDate', user_cardinality=u'0..1', seda_start_date=access_rule_seq)
             # binary data object
-            bdo = testutils.create_data_object(transfer, id=u"bdo1", user_cardinality=u'0..n',
+            bdo = testutils.create_data_object(transfer, user_cardinality=u'0..n',
                                                seda_algorithm=some_concept)
             create('SEDAFormatLitteral', seda_format_litteral=bdo)
             create('SEDAEncoding', seda_encoding_from=bdo)
@@ -662,6 +662,8 @@
 
             cnx.commit()
         self.transfer_eid = transfer.eid
+        self.bdo_eid = bdo.eid
+        self.au_eid = unit.eid
 
     def test_profile1(self):
         """Check a minimal SEDA profile validating BV2.0_min.xml."""
@@ -684,7 +686,8 @@
         # ensure profile's temporary id are exported in custom seda:profid attribute
         self.assertEqual(len(self.xpath(root, '//xs:attribute[@seda:profid]')), 2)
         # ensure they are properly referenced using 'default' attribute
-        references = self.xpath(root, '//xs:element[@default="bdo1"]')
+        xmlid = eid2xmlid(self.bdo_eid)
+        references = self.xpath(root, '//xs:element[@default="{}"]'.format(xmlid))
         self.assertEqual(len(references), 1)
         self.assertEqual(references[0].attrib['name'], 'DataObjectReferenceId')
         # ensure optional id are properly reinjected
@@ -713,7 +716,8 @@
         for attrdef in self.xpath(root, '//xs:attribute[@seda:profid]'):
             self.assertEqual(attrdef[0]['type'], 'ID')
         # ensure they are properly referenced using 'default' attribute
-        references = self.xpath(root, '//rng:element[@a:defaultValue="bdo1"]')
+        xmlid = eid2xmlid(self.bdo_eid)
+        references = self.xpath(root, '//rng:element[@a:defaultValue="{}"]'.format(xmlid))
         self.assertEqual(len(references), 1)
         self.assertEqual(references[0].attrib['name'], 'DataObjectReferenceId')
         self.assertEqual(references[0][0].attrib['type'], 'NCName')
@@ -777,7 +781,7 @@
                    seda_seq_appraisal_rule_rule=appraisal_rule_rule,
                    user_annotation=u'detruire le document')
 
-            _, _, unit_alt_seq = testutils.create_archive_unit(transfer, id=u'au1',
+            _, _, unit_alt_seq = testutils.create_archive_unit(transfer,
                                                                user_cardinality=u'1..n')
 
             content = create('SEDAContent',
@@ -813,7 +817,7 @@
                    reverse_seda_when=create('SEDAwhen'))
 
             # Add sub archive unit
-            _, _, subunit_alt_seq = testutils.create_archive_unit(unit_alt_seq, id=u'au2',
+            _, _, subunit_alt_seq = testutils.create_archive_unit(unit_alt_seq,
                                                                   user_cardinality=u'1..n')
             content = create('SEDAContent', seda_content=subunit_alt_seq)
             create('SEDATitle', seda_title=content)
@@ -833,7 +837,7 @@
 
             # Add minimal document to first level archive
             ref = create('SEDADataObjectReference', seda_data_object_reference=unit_alt_seq)
-            bdo = testutils.create_data_object(transfer, id=u"bdo1", user_cardinality=u'0..n',
+            bdo = testutils.create_data_object(transfer, user_cardinality=u'0..n',
                                                filename=u'this_is_the_filename.pdf',
                                                reverse_seda_data_object_reference_id=ref)
 
@@ -847,7 +851,7 @@
                    seda_encoding_to=concepts['6'])
 
             # Add another sub archive unit
-            _, _, subunit2_alt_seq = testutils.create_archive_unit(unit_alt_seq, id=u'au3',
+            _, _, subunit2_alt_seq = testutils.create_archive_unit(unit_alt_seq,
                                                                    user_cardinality=u'1..n')
             content = create('SEDAContent', seda_content=subunit2_alt_seq)
             create('SEDATitle', seda_title=content)
@@ -943,7 +947,7 @@
             create('SEDAStartDate', seda_start_date=access_rule_seq)
 
             subunit, subunit_alt, subunit_alt_seq = testutils.create_archive_unit(
-                unit_alt_seq, id=u'subunit')
+                unit_alt_seq)
             subcontent = create('SEDAContent', seda_content=subunit_alt_seq)
             create('SEDATitle', seda_title=subcontent)
             create('SEDATransferringAgencyArchiveUnitIdentifier',
--- a/test/test_schema.py	Tue Jan 10 16:07:35 2017 +0100
+++ b/test/test_schema.py	Tue Jan 10 17:51:45 2017 +0100
@@ -157,29 +157,6 @@
                                      'seda_seq_{0}_rule_rule'.format(rule_type): rule_rule})
                 cnx.commit()
 
-    def test_identifier_unicity_constraint(self):
-        """Check that an identifier must be unique within a profile."""
-        with self.admin_access.client_cnx() as cnx:
-            create = cnx.create_entity
-            transfer1 = create('SEDAArchiveTransfer', title=u'Profile1')
-            # Create an archive unit with an id
-            testutils.create_archive_unit(transfer1, id=u'id1')
-            cnx.commit()
-            # Creating binary data object with same id should fail
-            transfer1 = cnx.entity_from_eid(transfer1.eid)
-            bdo = create('SEDABinaryDataObject', id=u'id1', seda_binary_data_object=transfer1)
-            alt = create('SEDAAltBinaryDataObjectAttachment',
-                         reverse_seda_alt_binary_data_object_attachment=bdo)
-            create('SEDAUri', seda_uri=alt)
-            with self.assertRaises(ValidationError) as cm:
-                cnx.commit()
-            self.assertIn('identifier must be unique within a profile',
-                          cm.exception.errors.values().pop())
-            # Creating an archive unit in another profile with same id works
-            transfer2 = create('SEDAArchiveTransfer', title=u'Profile2')
-            testutils.create_archive_unit(transfer2, id=u'id1')
-            cnx.commit()
-
     def test_rdef_container_permissions(self):
         """Check that permissions are correctly set on rdefs between entity types contained in
         SEDAArchiveTransfer."""
@@ -246,7 +223,7 @@
             cnx.create_entity('SEDATitle', seda_title=content, title=u'transfer name'[::-1])
             cnx.create_entity('SEDAAccessRule', seda_access_rule=unit_alt_seq,
                               user_annotation=u'some annotation'[::-1])
-            testutils.create_data_object(transfer, id=u"bdo1", filename=u'fixed.txt'[::-1])
+            testutils.create_data_object(transfer, filename=u'fixed.txt'[::-1])
             cnx.commit()
 
             for search in ('name', 'annotation', 'fixed'):
--- a/test/test_views.py	Tue Jan 10 16:07:35 2017 +0100
+++ b/test/test_views.py	Tue Jan 10 17:51:45 2017 +0100
@@ -119,7 +119,7 @@
     def test_linkable_rset(self):
         with self.admin_access.repo_cnx() as cnx:
             transfer = cnx.create_entity('SEDAArchiveTransfer', title=u'Test widget')
-            bdo = cnx.create_entity('SEDABinaryDataObject', id=u'bdo',
+            bdo = cnx.create_entity('SEDABinaryDataObject',
                                     seda_binary_data_object=transfer)
             bdo_alt = cnx.create_entity('SEDAAltBinaryDataObjectAttachment',
                                         reverse_seda_alt_binary_data_object_attachment=bdo)
@@ -440,7 +440,7 @@
     def test_import_multiple_entities(self):
         with self.admin_access.cnx() as cnx:
             unit, unit_alt, unit_alt_seq = testutils.create_archive_unit(
-                None, cnx=cnx, id=u'new',
+                None, cnx=cnx,
                 user_cardinality=u'0..1', user_annotation=u'plouf')
             cnx.commit()
         to_clone = [self.unit_eid, unit.eid]
@@ -491,7 +491,7 @@
         with self.admin_access.cnx() as cnx:
             transfer = cnx.entity_from_eid(self.transfer_eid)
             archunit, _, _ = testutils.create_archive_unit(transfer)
-            archunit2, _, _ = testutils.create_archive_unit(transfer, id=u'pouet')
+            archunit2, _, _ = testutils.create_archive_unit(transfer)
             cnx.commit()
             archunit.cw_clear_all_caches()
             archunit.cw_adapt_to('IJQTree').reparent(archunit2.eid)
@@ -505,7 +505,7 @@
         with self.admin_access.cnx() as cnx:
             transfer = cnx.entity_from_eid(self.transfer_eid)
             archunit, _, alt_seq = testutils.create_archive_unit(transfer)
-            archunit2, _, alt_seq2 = testutils.create_archive_unit(transfer, id=u'pouet')
+            archunit2, _, alt_seq2 = testutils.create_archive_unit(transfer)
             bdo = testutils.create_data_object(transfer)
             ref = cnx.create_entity('SEDADataObjectReference',
                                     seda_data_object_reference=alt_seq,
--- a/test/testutils.py	Tue Jan 10 16:07:35 2017 +0100
+++ b/test/testutils.py	Tue Jan 10 17:51:45 2017 +0100
@@ -33,7 +33,6 @@
     else (archive unit, alternative, reference).
     """
     cnx = kwargs.pop('cnx', getattr(parent, '_cw', None))
-    kwargs.setdefault('id', u'au1')
     au = cnx.create_entity('SEDAArchiveUnit', seda_archive_unit=parent, **kwargs)
     alt = cnx.create_entity('SEDAAltArchiveUnitArchiveUnitRefId',
                             reverse_seda_alt_archive_unit_archive_unit_ref_id=au)
@@ -48,7 +47,6 @@
 
 def create_data_object(parent, **kwargs):
     cnx = getattr(parent, '_cw', None)
-    kwargs.setdefault('id', u'bdo1')
     if parent.cw_etype == 'SEDAArchiveTransfer':
         kwargs['seda_binary_data_object'] = parent
     bdo = cnx.create_entity('SEDABinaryDataObject', **kwargs)
--- a/views/uicfg.py	Tue Jan 10 16:07:35 2017 +0100
+++ b/views/uicfg.py	Tue Jan 10 17:51:45 2017 +0100
@@ -546,7 +546,6 @@
 affk.tag_subject_of(('*', 'seda_classification_rule_code_list_version_to', '*'), {'label': 'value'})
 affk.tag_subject_of(('*', 'seda_relationship_code_list_version_to', '*'), {'label': 'value'})
 affk.tag_subject_of(('*', 'filename', '*'), {'widget': fw.TextInput({'size': 80})})
-affk.tag_subject_of(('*', 'id', '*'), {'widget': fw.TextInput({'size': 80})})
 affk.tag_subject_of(('*', 'compressed', '*'), {'allow_none': True})
 affk.tag_subject_of(('*', 'service_level', '*'), {'widget': fw.TextInput({'size': 80})})
 affk.tag_subject_of(('*', 'classification_owner', '*'), {'widget': fw.TextInput({'size': 80})})
@@ -1148,7 +1147,6 @@
 rec.tag_object_of(('*', 'seda_relationship_code_list_version_from', '*'), {'novalue_label': _('<unauthorized>')})
 rec.tag_subject_of(('*', 'seda_relationship_code_list_version_to', '*'), {'novalue_label': _('<no value specified>')})
 rec.tag_subject_of(('*', 'filename', '*'), {'novalue_label': _('<no value specified>')})
-rec.tag_subject_of(('*', 'id', '*'), {'novalue_label': _('<no value specified>')})
 rec.tag_object_of(('*', 'seda_data_object_version_from', '*'), {'novalue_label': _('<unauthorized>')})
 rec.tag_subject_of(('*', 'seda_data_object_version_to', '*'), {'novalue_label': _('<no value specified>')})
 rec.tag_subject_of(('*', 'seda_alt_binary_data_object_attachment', '*'), {'novalue_label': _('<no value specified>')})
@@ -1308,14 +1306,14 @@
 pvds.set_fields_order('SEDAComment', ['user_cardinality', 'comment', 'user_annotation'])
 affk.set_fields_order('SEDAArchivalAgreement', ['user_cardinality', 'archival_agreement', 'user_annotation'])
 pvds.set_fields_order('SEDAArchivalAgreement', ['user_cardinality', 'archival_agreement', 'user_annotation'])
-affk.set_fields_order('SEDABinaryDataObject', ['id', 'filename', 'user_cardinality', 'user_annotation'])
-pvds.set_fields_order('SEDABinaryDataObject', ['id', 'filename', 'user_cardinality', 'user_annotation'])
-affk.set_fields_order('SEDAPhysicalDataObject', ['id', 'user_cardinality', 'user_annotation'])
-pvds.set_fields_order('SEDAPhysicalDataObject', ['id', 'user_cardinality', 'user_annotation'])
+affk.set_fields_order('SEDABinaryDataObject', ['filename', 'user_cardinality', 'user_annotation'])
+pvds.set_fields_order('SEDABinaryDataObject', ['filename', 'user_cardinality', 'user_annotation'])
+affk.set_fields_order('SEDAPhysicalDataObject', ['user_cardinality', 'user_annotation'])
+pvds.set_fields_order('SEDAPhysicalDataObject', ['user_cardinality', 'user_annotation'])
 affk.set_fields_order('SEDACompressed', ['user_cardinality', 'compressed', 'user_annotation'])
 pvds.set_fields_order('SEDACompressed', ['user_cardinality', 'compressed', 'user_annotation'])
-affk.set_fields_order('SEDAArchiveUnit', ['id', 'user_cardinality', 'user_annotation'])
-pvds.set_fields_order('SEDAArchiveUnit', ['id', 'user_cardinality', 'user_annotation'])
+affk.set_fields_order('SEDAArchiveUnit', ['user_cardinality', 'user_annotation'])
+pvds.set_fields_order('SEDAArchiveUnit', ['user_cardinality', 'user_annotation'])
 affk.set_fields_order('SEDAServiceLevel', ['user_cardinality', 'service_level', 'user_annotation'])
 pvds.set_fields_order('SEDAServiceLevel', ['user_cardinality', 'service_level', 'user_annotation'])
 affk.set_fields_order('SEDAClassificationRule', ['user_cardinality', 'classification_owner', 'user_annotation'])
--- a/xsd2entities.py	Tue Jan 10 16:07:35 2017 +0100
+++ b/xsd2entities.py	Tue Jan 10 17:51:45 2017 +0100
@@ -56,17 +56,13 @@
 
     def entity_class_for_e_type_mapping(self, mapping):
         attributes = ordered_attributes(mapping)
-        if mapping.etype == 'SEDAArchiveTransfer':
-            attributes = ordered_attributes(mapping)
-            value_attribute = None
+        value_attribute = None
+        if mapping.etype in ('SEDAArchiveUnit', 'SEDABinaryDataObject', 'SEDAPhysicalDataObject'):
+            pass
         elif mapping.attributes:
-            attributes = ordered_attributes(mapping)
             value_attribute = next(iter(mapping.attributes))
-        else:
-            attributes = []
-            if len(mapping.cards) > 1:
-                attributes.append('user_cardinality')
-            value_attribute = None
+        elif mapping.cards and len(mapping.cards) > 1:
+            attributes = ['user_cardinality']
         return TEMPLATE.format(etype=mapping.etype,
                                attributes=attributes,
                                value_attribute=repr(value_attribute))
--- a/xsd2uicfg.py	Tue Jan 10 16:07:35 2017 +0100
+++ b/xsd2uicfg.py	Tue Jan 10 17:51:45 2017 +0100
@@ -139,6 +139,8 @@
 
     def autoform_field_kwargs_for_e_type_mapping(self, mapping):
         for rtype, target_etype in sorted(mapping.attributes.items()):
+            if rtype == 'id':
+                continue
             if target_etype == 'String' and ('affk', rtype) not in self._processed:
                 self._processed.add(('affk', rtype))
                 yield rtype, 'subject', {'widget': Code("fw.TextInput({'size': 80})")}
@@ -164,6 +166,8 @@
 
     def reledit_ctrl_for_e_type_mapping(self, mapping):
         for rtype, target_etype in sorted(mapping.attributes.items()):
+            if rtype == 'id':
+                continue
             if ('rec', rtype) not in self._processed:
                 self._processed.add(('rec', rtype))
                 yield rtype, 'subject', 'no value specified'
@@ -206,16 +210,16 @@
 def ordered_attributes(mapping):
     """Given an ETypeMapping, return a list of its attributes sorted by desired order of appearance
     """
-    attributes = list(mapping.attributes)
+    attributes = [attr for attr in mapping.attributes if attr != 'id']
     if mapping.etype == 'SEDAArchiveTransfer':
         attributes.append('title')
         attributes.append('user_annotation')
+    elif mapping.etype in ('SEDAArchiveUnit', 'SEDABinaryDataObject', 'SEDAPhysicalDataObject'):
+        attributes.append('user_cardinality')
+        attributes.append('user_annotation')
     elif attributes:
         if len(mapping.cards) > 1:
-            if attributes[0] == 'id':
-                attributes.append('user_cardinality')
-            else:
-                attributes.insert(0, 'user_cardinality')
+            attributes.insert(0, 'user_cardinality')
             attributes.append('user_annotation')
     return attributes
 
--- a/xsd2yams.py	Tue Jan 10 16:07:35 2017 +0100
+++ b/xsd2yams.py	Tue Jan 10 17:51:45 2017 +0100
@@ -511,18 +511,10 @@
     u""""""
 '''.format(mapping.etype)
         for attrname, attrtype in sorted(mapping.attributes.items()):
+            if attrname == 'id':
+                continue
             args = u''
-            if attrname == 'id':
-                # XXX should be globaly unique in a profile
-                args = (u"required=True, "
-                        "description=_('Default identifier that will be used to "
-                        "handle references. This is not necessarily the identifier that will "
-                        "appear in the final sheet.'), "
-                        "constraints=["
-                        "RQLConstraint('NOT EXISTS(S id I, X id I, S container C, X container C, "
-                        "NOT S identity X)', msg=_('identifier must be unique within a profile'))"
-                        "]")
-            elif attrtype == 'String':
+            if attrtype == 'String':
                 args = 'fulltextindexed=True'
             code += '    {0} = {1}({2})\n'.format(attrname, attrtype, args)
         return code + '\n\n'