Ejemplo n.º 1
0
    def create(layer_wrapper: LayerWrapper, relation_manager: QgsRelationManager,
               relation: QgsRelation) -> 'RelationalLayerWrapper':

        referencing_layer: QgsVectorLayer = relation.referencingLayer()
        try:
            other_relation: QgsRelation = [r for r in relation_manager.referencingRelations(referencing_layer)
                                           if r.id() != relation.id()][0]
        except KeyError:
            raise QaavaLayerError(tr('Relation error'), bar_msg(
                tr('Relation {} does not contain referencing another layer', relation.name())))

        relation_layer_wrapper = LayerWrapper.from_qgs_layer(referencing_layer)
        a_pk: QgsField = referencing_layer.fields().toList()[relation.referencingFields()[0]]
        m_pk_a: QgsField = layer_wrapper.get_layer().fields().toList()[relation.referencedFields()[0]]

        fw_m_a = FieldWrapper.from_layer_wrapper(relation_layer_wrapper, a_pk, set(), '')
        fw_a = FieldWrapper.from_layer_wrapper(layer_wrapper, m_pk_a, set(), '')

        other_layer: QgsVectorLayer = other_relation.referencedLayer()
        b_pk = other_layer.fields().toList()[other_relation.referencedFields()[0]]
        m_pk_b = referencing_layer.fields().toList()[other_relation.referencingFields()[0]]

        fw_m_b = FieldWrapper.from_layer_wrapper(relation_layer_wrapper, m_pk_b, set(), '')
        other_layer_wrapper = LayerWrapper.from_qgs_layer(other_layer, relation_layer_wrapper, fw_m_b)

        fw_b = FieldWrapper.from_layer_wrapper(other_layer_wrapper, b_pk, set(), '')

        return RelationalLayerWrapper(referencing_layer.name(), other_layer.name(), layer_wrapper, fw_m_a,
                                      fw_a, fw_m_b, fw_b)
Ejemplo n.º 2
0
def import_in_qgis(gmlas_uri, provider, schema=None):
    """Imports layers from a GMLAS file in QGIS with relations and editor widgets

    @param gmlas_uri connection parameters
    @param provider name of the QGIS provider that handles gmlas_uri parameters (postgresql or spatialite)
    @param schema name of the PostgreSQL schema where tables and metadata tables are
    """
    if schema is not None:
        schema_s = schema + "."
    else:
        schema_s = ""

    ogr.UseExceptions()
    drv = ogr.GetDriverByName(provider)
    ds = drv.Open(gmlas_uri)
    if ds is None:
        raise RuntimeError("Problem opening {}".format(gmlas_uri))

    # get list of layers
    sql = "select o.*, g.f_geometry_column, g.srid from {}_ogr_layers_metadata o left join geometry_columns g on g.f_table_name = o.layer_name".format(
        schema_s)

    couches = ds.ExecuteSQL(sql)
    layers = {}
    for f in couches:
        ln = f.GetField("layer_name")
        if ln not in layers:
            layers[ln] = {
                "uid": f.GetField("layer_pkid_name"),
                "category": f.GetField("layer_category"),
                "xpath": f.GetField("layer_xpath"),
                "parent_pkid": f.GetField("layer_parent_pkid_name"),
                "srid": f.GetField("srid"),
                "geometry_column": f.GetField("f_geometry_column"),
                "1_n": [],  # 1:N relations
                "layer_id": None,
                "layer_name": ln,
                "layer": None,
                "fields": [],
            }
        else:
            # additional geometry columns
            g = f.GetField("f_geometry_column")
            k = "{} ({})".format(ln, g)
            layers[k] = dict(layers[ln])
            layers[k]["geometry_column"] = g

    # collect fields with xlink:href
    href_fields = {}
    for ln, layer in layers.items():
        layer_name = layer["layer_name"]
        for f in ds.ExecuteSQL(
                "select field_name, field_xpath from {}_ogr_fields_metadata where layer_name='{}'"
                .format(schema_s, layer_name)):
            field_name, field_xpath = f.GetField("field_name"), f.GetField(
                "field_xpath")
            if field_xpath and field_xpath.endswith("@xlink:href"):
                if ln not in href_fields:
                    href_fields[ln] = []
                href_fields[ln].append(field_name)

    # with unknown srid, don't ask for each layer, set to a default
    settings = QgsSettings()
    projection_behavior = settings.value("Projections/defaultBehavior")
    projection_default = settings.value("Projections/layerDefaultCrs")
    settings.setValue("Projections/defaultBehavior", "useGlobal")
    settings.setValue("Projections/layerDefaultCrs", "EPSG:4326")

    # add layers
    crs = QgsCoordinateReferenceSystem("EPSG:4326")
    for ln in sorted(layers.keys()):
        lyr = layers[ln]
        g_column = lyr["geometry_column"] or None
        couches = _qgis_layer(
            gmlas_uri,
            schema,
            lyr["layer_name"],
            g_column,
            provider,
            ln,
            lyr["xpath"],
            lyr["uid"],
        )
        if not couches.isValid():
            raise RuntimeError("Problem loading layer {} with {}".format(
                ln, couches.source()))
        if g_column is not None:
            if lyr["srid"]:
                crs = QgsCoordinateReferenceSystem("EPSG:{}".format(
                    lyr["srid"]))
            couches.setCrs(crs)
        QgsProject.instance().addMapLayer(couches)
        layers[ln]["layer_id"] = couches.id()
        layers[ln]["layer"] = couches
        # save fields which represent a xlink:href
        if ln in href_fields:
            couches.setCustomProperty("href_fields", href_fields[ln])
        # save gmlas_uri
        couches.setCustomProperty("ogr_uri", gmlas_uri)
        couches.setCustomProperty("ogr_schema", schema)

        # change icon the layer has a custom viewer
        xpath = no_ns(couches.customProperty("xpath", ""))
        for viewer_cls, _ in get_custom_viewers().values():
            tag = no_prefix(viewer_cls.xml_tag())
            if tag == xpath:
                lg = CustomViewerLegend(viewer_cls.name(), viewer_cls.icon())
                couches.setLegend(lg)

    # restore settings
    settings.setValue("Projections/defaultBehavior", projection_behavior)
    settings.setValue("Projections/layerDefaultCrs", projection_default)

    # add 1:1 relations
    relations_1_1 = []
    sql = """
select
  layer_name, field_name, field_related_layer, r.child_pkid
from
  {0}_ogr_fields_metadata f
  join {0}_ogr_layer_relationships r
    on r.parent_layer = f.layer_name
   and r.parent_element_name = f.field_name
where
  field_category in ('PATH_TO_CHILD_ELEMENT_WITH_LINK', 'PATH_TO_CHILD_ELEMENT_NO_LINK')
  and field_max_occurs=1
""".format(schema_s)
    couches = ds.ExecuteSQL(sql)
    if couches is not None:
        for f in couches:
            rel = QgsRelation()
            rel.setId("1_1_" + f.GetField("layer_name") + "_" +
                      f.GetField("field_name"))
            rel.setName("1_1_" + f.GetField("layer_name") + "_" +
                        f.GetField("field_name"))
            # parent layer
            rel.setReferencingLayer(
                layers[f.GetField("layer_name")]["layer_id"])
            # child layer
            rel.setReferencedLayer(
                layers[f.GetField("field_related_layer")]["layer_id"])
            # parent, child
            rel.addFieldPair(f.GetField("field_name"),
                             f.GetField("child_pkid"))
            # rel.generateId()
            if rel.isValid():
                relations_1_1.append(rel)

    # add 1:N relations
    relations_1_n = []
    sql = """
select
  layer_name, r.parent_pkid, field_related_layer as child_layer, r.child_pkid
from
  {0}_ogr_fields_metadata f
  join {0}_ogr_layer_relationships r
    on r.parent_layer = f.layer_name
   and r.child_layer = f.field_related_layer
where
  field_category in ('PATH_TO_CHILD_ELEMENT_WITH_LINK', 'PATH_TO_CHILD_ELEMENT_NO_LINK')
  and field_max_occurs>1
-- junctions - 1st way
union all
select
  layer_name, r.parent_pkid, field_junction_layer as child_layer, 'parent_pkid' as child_pkid
from
  {0}_ogr_fields_metadata f
  join {0}_ogr_layer_relationships r
    on r.parent_layer = f.layer_name
   and r.child_layer = f.field_related_layer
where
  field_category = 'PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE'
-- junctions - 2nd way
union all
select
  field_related_layer as layer_name, r.child_pkid, field_junction_layer as child_layer, 'child_pkid' as child_pkid
from
  {0}_ogr_fields_metadata f
  join {0}_ogr_layer_relationships r
    on r.parent_layer = f.layer_name
   and r.child_layer = f.field_related_layer
where
  field_category = 'PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE'
""".format(schema_s)
    couches = ds.ExecuteSQL(sql)
    if couches is not None:
        for f in couches:
            parent_layer = f.GetField("layer_name")
            child_layer = f.GetField("child_layer")
            if parent_layer not in layers or child_layer not in layers:
                continue
            rel = QgsRelation()
            rel.setId("1_n_" + f.GetField("layer_name") + "_" +
                      f.GetField("child_layer") + "_" +
                      f.GetField("parent_pkid") + "_" +
                      f.GetField("child_pkid"))
            rel.setName(f.GetField("child_layer"))
            # parent layer
            rel.setReferencedLayer(layers[parent_layer]["layer_id"])
            # child layer
            rel.setReferencingLayer(layers[child_layer]["layer_id"])
            # parent, child
            rel.addFieldPair(f.GetField("child_pkid"),
                             f.GetField("parent_pkid"))
            # rel.addFieldPair(f.GetField('child_pkid'), 'ogc_fid')
            if rel.isValid():
                relations_1_n.append(rel)
                # add relation to layer
                layers[f.GetField("layer_name")]["1_n"].append(rel)

    for rel in relations_1_1 + relations_1_n:
        QgsProject.instance().relationManager().addRelation(rel)

    # add "show form" option to 1:1 relations
    for rel in relations_1_1:
        couches = rel.referencingLayer()
        idx = rel.referencingFields()[0]
        s = QgsEditorWidgetSetup(
            "RelationReference",
            {
                "AllowNULL": False,
                "ReadOnly": True,
                "Relation": rel.id(),
                "OrderByValue": False,
                "MapIdentification": False,
                "AllowAddFeatures": False,
                "ShowForm": True,
            },
        )
        couches.setEditorWidgetSetup(idx, s)

    # setup form for layers
    for layer, lyr in layers.items():
        couche = lyr["layer"]
        fc = couche.editFormConfig()
        fc.clearTabs()
        fc.setLayout(QgsEditFormConfig.TabLayout)
        # Add fields
        c = QgsAttributeEditorContainer("Main", fc.invisibleRootContainer())
        c.setIsGroupBox(False)  # a tab
        for idx, f in enumerate(couche.fields()):
            c.addChildElement(QgsAttributeEditorField(f.name(), idx, c))
        fc.addTab(c)

        # Add 1:N relations
        c_1_n = QgsAttributeEditorContainer("1:N links",
                                            fc.invisibleRootContainer())
        c_1_n.setIsGroupBox(False)  # a tab
        fc.addTab(c_1_n)

        for rel in lyr["1_n"]:
            c_1_n.addChildElement(
                QgsAttributeEditorRelation(rel.name(), rel, c_1_n))

        couche.setEditFormConfig(fc)

        install_viewer_on_feature_form(couche)
def import_in_qgis(gmlas_uri, provider, schema = None):
    """Imports layers from a GMLAS file in QGIS with relations and editor widgets

    @param gmlas_uri connection parameters
    @param provider name of the QGIS provider that handles gmlas_uri parameters (postgresql or spatialite)
    @param schema name of the PostgreSQL schema where tables and metadata tables are
    """
    if schema is not None:
        schema_s = schema + "."
    else:
        schema_s = ""

    ogr.UseExceptions()
    drv = ogr.GetDriverByName(provider)
    ds = drv.Open(gmlas_uri)
    if ds is None:
        raise RuntimeError("Problem opening {}".format(gmlas_uri))

    # get list of layers
    sql = "select o.*, g.f_geometry_column, g.srid from {}_ogr_layers_metadata o left join geometry_columns g on g.f_table_name = o.layer_name".format(schema_s)
    
    l = ds.ExecuteSQL(sql)
    layers = {}
    for f in l:
        ln = f.GetField("layer_name")
        if ln not in layers:
            layers[ln] = {
                'uid': f.GetField("layer_pkid_name"),
                'category': f.GetField("layer_category"),
                'xpath': f.GetField("layer_xpath"),
                'parent_pkid': f.GetField("layer_parent_pkid_name"),
                'srid': f.GetField("srid"),
                'geometry_column': f.GetField("f_geometry_column"),
                '1_n' : [], # 1:N relations
                'layer_id': None,
                'layer_name' : ln,
                'layer': None,
                'fields' : []}
        else:
            # additional geometry columns
            g = f.GetField("f_geometry_column")
            k = "{} ({})".format(ln, g)
            layers[k] = dict(layers[ln])
            layers[k]["geometry_column"] = g

    # collect fields with xlink:href
    href_fields = {}
    for ln, layer in layers.items():
        layer_name = layer["layer_name"]
        for f in ds.ExecuteSQL("select field_name, field_xpath from {}_ogr_fields_metadata where layer_name='{}'".format(schema_s, layer_name)):
            field_name, field_xpath = f.GetField("field_name"), f.GetField("field_xpath")
            if field_xpath and field_xpath.endswith("@xlink:href"):
                if ln not in href_fields:
                    href_fields[ln] = []
                href_fields[ln].append(field_name)

    # with unknown srid, don't ask for each layer, set to a default
    settings = QgsSettings()
    projection_behavior = settings.value("Projections/defaultBehavior")
    projection_default = settings.value("Projections/layerDefaultCrs")
    settings.setValue("Projections/defaultBehavior", "useGlobal")
    settings.setValue("Projections/layerDefaultCrs", "EPSG:4326")

    # add layers
    crs = QgsCoordinateReferenceSystem("EPSG:4326")
    for ln in sorted(layers.keys()):
        lyr = layers[ln]
        g_column = lyr["geometry_column"] or None
        l = _qgis_layer(gmlas_uri, schema, lyr["layer_name"], g_column, provider, ln, lyr["xpath"], lyr["uid"])
        if not l.isValid():
            raise RuntimeError("Problem loading layer {} with {}".format(ln, l.source()))
        if g_column is not None:
            if lyr["srid"]:
                crs = QgsCoordinateReferenceSystem("EPSG:{}".format(lyr["srid"]))
            l.setCrs(crs)
        QgsProject.instance().addMapLayer(l)
        layers[ln]['layer_id'] = l.id()
        layers[ln]['layer'] = l
        # save fields which represent a xlink:href
        if ln in href_fields:
            l.setCustomProperty("href_fields", href_fields[ln])
        # save gmlas_uri
        l.setCustomProperty("ogr_uri", gmlas_uri)
        l.setCustomProperty("ogr_schema", schema)

        # change icon the layer has a custom viewer
        xpath = no_ns(l.customProperty("xpath", ""))
        for viewer_cls, _ in get_custom_viewers().values():
            tag = no_prefix(viewer_cls.xml_tag())
            if tag == xpath:
                lg = CustomViewerLegend(viewer_cls.name(), viewer_cls.icon())
                l.setLegend(lg)

    # restore settings
    settings.setValue("Projections/defaultBehavior", projection_behavior)
    settings.setValue("Projections/layerDefaultCrs", projection_default)

    # add 1:1 relations
    relations_1_1 = []
    sql = """
select
  layer_name, field_name, field_related_layer, r.child_pkid
from
  {0}_ogr_fields_metadata f
  join {0}_ogr_layer_relationships r
    on r.parent_layer = f.layer_name
   and r.parent_element_name = f.field_name
where
  field_category in ('PATH_TO_CHILD_ELEMENT_WITH_LINK', 'PATH_TO_CHILD_ELEMENT_NO_LINK')
  and field_max_occurs=1
""".format(schema_s)
    l = ds.ExecuteSQL(sql)
    if l is not None:
        for f in l:
            rel = QgsRelation()
            rel.setId('1_1_' + f.GetField('layer_name') + '_' + f.GetField('field_name'))
            rel.setName('1_1_' + f.GetField('layer_name') + '_' + f.GetField('field_name'))
            # parent layer
            rel.setReferencingLayer(layers[f.GetField('layer_name')]['layer_id'])
            # child layer
            rel.setReferencedLayer(layers[f.GetField('field_related_layer')]['layer_id'])
            # parent, child
            rel.addFieldPair(f.GetField('field_name'), f.GetField('child_pkid'))
            #rel.generateId()
            if rel.isValid():
                relations_1_1.append(rel)

    # add 1:N relations
    relations_1_n = []
    sql = """
select
  layer_name, r.parent_pkid, field_related_layer as child_layer, r.child_pkid
from
  {0}_ogr_fields_metadata f
  join {0}_ogr_layer_relationships r
    on r.parent_layer = f.layer_name
   and r.child_layer = f.field_related_layer
where
  field_category in ('PATH_TO_CHILD_ELEMENT_WITH_LINK', 'PATH_TO_CHILD_ELEMENT_NO_LINK')
  and field_max_occurs>1
-- junctions - 1st way
union all
select
  layer_name, r.parent_pkid, field_junction_layer as child_layer, 'parent_pkid' as child_pkid
from
  {0}_ogr_fields_metadata f
  join {0}_ogr_layer_relationships r
    on r.parent_layer = f.layer_name
   and r.child_layer = f.field_related_layer
where
  field_category = 'PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE'
-- junctions - 2nd way
union all
select
  field_related_layer as layer_name, r.child_pkid, field_junction_layer as child_layer, 'child_pkid' as child_pkid
from
  {0}_ogr_fields_metadata f
  join {0}_ogr_layer_relationships r
    on r.parent_layer = f.layer_name
   and r.child_layer = f.field_related_layer
where
  field_category = 'PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE'
""".format(schema_s)
    l = ds.ExecuteSQL(sql)
    if l is not None:
        for f in l:
            parent_layer = f.GetField("layer_name")
            child_layer = f.GetField("child_layer")
            if parent_layer not in layers or child_layer not in layers:
                continue
            rel = QgsRelation()
            rel.setId('1_n_' + f.GetField('layer_name') + '_' + f.GetField('child_layer') + '_' + f.GetField('parent_pkid') + '_' + f.GetField('child_pkid'))
            rel.setName(f.GetField('child_layer'))
            # parent layer
            rel.setReferencedLayer(layers[parent_layer]['layer_id'])
            # child layer
            rel.setReferencingLayer(layers[child_layer]['layer_id'])
            # parent, child
            rel.addFieldPair(f.GetField('child_pkid'), f.GetField('parent_pkid'))
            #rel.addFieldPair(f.GetField('child_pkid'), 'ogc_fid')
            if rel.isValid():
                relations_1_n.append(rel)
                # add relation to layer
                layers[f.GetField('layer_name')]['1_n'].append(rel)

    for rel in relations_1_1 + relations_1_n:
        QgsProject.instance().relationManager().addRelation(rel)

    # add "show form" option to 1:1 relations
    for rel in relations_1_1:
        l = rel.referencingLayer()
        idx = rel.referencingFields()[0]
        s = QgsEditorWidgetSetup("RelationReference", {'AllowNULL': False,
                                      'ReadOnly': True,
                                      'Relation': rel.id(),
                                      'OrderByValue': False,
                                      'MapIdentification': False,
                                      'AllowAddFeatures': False,
                                      'ShowForm': True})
        l.setEditorWidgetSetup(idx, s)

    # setup form for layers
    for layer, lyr in layers.items():
        l = lyr['layer']
        fc = l.editFormConfig()
        fc.clearTabs()
        fc.setLayout(QgsEditFormConfig.TabLayout)
        # Add fields
        c = QgsAttributeEditorContainer("Main", fc.invisibleRootContainer())
        c.setIsGroupBox(False) # a tab
        for idx, f in enumerate(l.fields()):
            c.addChildElement(QgsAttributeEditorField(f.name(), idx, c))
        fc.addTab(c)

        # Add 1:N relations
        c_1_n = QgsAttributeEditorContainer("1:N links", fc.invisibleRootContainer())
        c_1_n.setIsGroupBox(False) # a tab
        fc.addTab(c_1_n)
        
        for rel in lyr['1_n']:
            c_1_n.addChildElement(QgsAttributeEditorRelation(rel.name(), rel, c_1_n))

        l.setEditFormConfig(fc)

        install_viewer_on_feature_form(l)
def import_in_qgis(gmlas_uri, provider, schema=None):
    """Imports layers from a GMLAS file in QGIS with relations and editor widgets

    @param gmlas_uri connection parameters
    @param provider name of the QGIS provider that handles gmlas_uri parameters (postgresql or spatialite)
    @param schema name of the PostgreSQL schema where tables and metadata tables are
    """
    if schema is not None:
        schema_s = schema + "."
    else:
        schema_s = ""

    ogr.UseExceptions()
    drv = ogr.GetDriverByName(provider)
    ds = drv.Open(gmlas_uri)
    if ds is None:
        raise RuntimeError("Problem opening {}".format(gmlas_uri))

    # get list of layers
    sql = "select o.*, g.f_geometry_column, g.srid from {}_ogr_layers_metadata o left join geometry_columns g on g.f_table_name = o.layer_name".format(
        schema_s)

    l = ds.ExecuteSQL(sql)
    layers = {}
    for f in l:
        ln = f.GetField("layer_name")
        if ln not in layers:
            layers[ln] = {
                'uid': f.GetField("layer_pkid_name"),
                'category': f.GetField("layer_category"),
                'xpath': f.GetField("layer_xpath"),
                'parent_pkid': f.GetField("layer_parent_pkid_name"),
                'srid': f.GetField("srid"),
                'geometry_column': f.GetField("f_geometry_column"),
                '1_n': [],  # 1:N relations
                'layer_id': None,
                'layer_name': ln,
                'layer': None,
                'fields': []
            }
        else:
            # additional geometry columns
            g = f.GetField("f_geometry_column")
            k = "{} ({})".format(ln, g)
            layers[k] = dict(layers[ln])
            layers[k]["geometry_column"] = g

    crs = QgsCoordinateReferenceSystem("EPSG:4326")
    for ln in sorted(layers.keys()):
        lyr = layers[ln]
        g_column = lyr["geometry_column"] or None
        l = _qgis_layer(gmlas_uri,
                        schema,
                        lyr["layer_name"],
                        g_column,
                        provider,
                        qgis_layer_name=ln)
        if not l.isValid():
            raise RuntimeError("Problem loading layer {} with {}".format(
                ln, l.source()))
        if lyr["srid"]:
            crs = QgsCoordinateReferenceSystem("EPSG:{}".format(lyr["srid"]))
        l.setCrs(crs)
        QgsProject.instance().addMapLayer(l)
        layers[ln]['layer_id'] = l.id()
        layers[ln]['layer'] = l

    # add 1:1 relations
    relations_1_1 = []
    sql = """
select
  layer_name, field_name, field_related_layer, r.child_pkid
from
  {0}_ogr_fields_metadata f
  join {0}_ogr_layer_relationships r
    on r.parent_layer = f.layer_name
   and r.parent_element_name = f.field_name
where
  field_category in ('PATH_TO_CHILD_ELEMENT_WITH_LINK', 'PATH_TO_CHILD_ELEMENT_NO_LINK')
  and field_max_occurs=1
""".format(schema_s)
    l = ds.ExecuteSQL(sql)
    if l is not None:
        for f in l:
            rel = QgsRelation()
            rel.setId('1_1_' + f.GetField('layer_name') + '_' +
                      f.GetField('field_name'))
            rel.setName('1_1_' + f.GetField('layer_name') + '_' +
                        f.GetField('field_name'))
            # parent layer
            rel.setReferencingLayer(
                layers[f.GetField('layer_name')]['layer_id'])
            # child layer
            rel.setReferencedLayer(
                layers[f.GetField('field_related_layer')]['layer_id'])
            # parent, child
            rel.addFieldPair(f.GetField('field_name'),
                             f.GetField('child_pkid'))
            #rel.generateId()
            if rel.isValid():
                relations_1_1.append(rel)

    # add 1:N relations
    relations_1_n = []
    sql = """
select
  layer_name, r.parent_pkid, field_related_layer as child_layer, r.child_pkid
from
  {0}_ogr_fields_metadata f
  join {0}_ogr_layer_relationships r
    on r.parent_layer = f.layer_name
   and r.child_layer = f.field_related_layer
where
  field_category in ('PATH_TO_CHILD_ELEMENT_WITH_LINK', 'PATH_TO_CHILD_ELEMENT_NO_LINK')
  and field_max_occurs>1
-- junctions - 1st way
union all
select
  layer_name, r.parent_pkid, field_junction_layer as child_layer, 'parent_pkid' as child_pkid
from
  {0}_ogr_fields_metadata f
  join {0}_ogr_layer_relationships r
    on r.parent_layer = f.layer_name
   and r.child_layer = f.field_related_layer
where
  field_category = 'PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE'
-- junctions - 2nd way
union all
select
  field_related_layer as layer_name, r.child_pkid, field_junction_layer as child_layer, 'child_pkid' as child_pkid
from
  {0}_ogr_fields_metadata f
  join {0}_ogr_layer_relationships r
    on r.parent_layer = f.layer_name
   and r.child_layer = f.field_related_layer
where
  field_category = 'PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE'
""".format(schema_s)
    l = ds.ExecuteSQL(sql)
    if l is not None:
        for f in l:
            rel = QgsRelation()
            rel.setId('1_n_' + f.GetField('layer_name') + '_' +
                      f.GetField('child_layer') + '_' +
                      f.GetField('parent_pkid') + '_' +
                      f.GetField('child_pkid'))
            rel.setName(f.GetField('child_layer'))
            # parent layer
            rel.setReferencedLayer(
                layers[f.GetField('layer_name')]['layer_id'])
            # child layer
            rel.setReferencingLayer(
                layers[f.GetField('child_layer')]['layer_id'])
            # parent, child
            rel.addFieldPair(f.GetField('child_pkid'),
                             f.GetField('parent_pkid'))
            #rel.addFieldPair(f.GetField('child_pkid'), 'ogc_fid')
            if rel.isValid():
                relations_1_n.append(rel)
                # add relation to layer
                layers[f.GetField('layer_name')]['1_n'].append(rel)

    QgsProject.instance().relationManager().setRelations(relations_1_1 +
                                                         relations_1_n)

    # add "show form" option to 1:1 relations
    for rel in relations_1_1:
        l = rel.referencingLayer()
        idx = rel.referencingFields()[0]
        s = QgsEditorWidgetSetup(
            "RelationReference", {
                'AllowNULL': False,
                'ReadOnly': True,
                'Relation': rel.id(),
                'OrderByValue': False,
                'MapIdentification': False,
                'AllowAddFeatures': False,
                'ShowForm': True
            })
        l.setEditorWidgetSetup(idx, s)

    # setup form for layers
    for layer, lyr in layers.items():
        l = lyr['layer']
        fc = l.editFormConfig()
        fc.clearTabs()
        fc.setLayout(QgsEditFormConfig.TabLayout)
        # Add fields
        c = QgsAttributeEditorContainer("Main", fc.invisibleRootContainer())
        c.setIsGroupBox(False)  # a tab
        for idx, f in enumerate(l.fields()):
            c.addChildElement(QgsAttributeEditorField(f.name(), idx, c))
        fc.addTab(c)

        # Add 1:N relations
        c_1_n = QgsAttributeEditorContainer("1:N links",
                                            fc.invisibleRootContainer())
        c_1_n.setIsGroupBox(False)  # a tab
        fc.addTab(c_1_n)

        for rel in lyr['1_n']:
            c_1_n.addChildElement(
                QgsAttributeEditorRelation(rel.name(), rel, c_1_n))

        l.setEditFormConfig(fc)