Skip to content

Commit 740319e

Browse files
authored
Fix reference when field and resource name is changed (#1577)
* Fix names of primary and foreign keys when updated * Add comment Add comment Field type check while updating field name * Fix references when resource and field name is updated
1 parent 54d05ab commit 740319e

File tree

4 files changed

+149
-2
lines changed

4 files changed

+149
-2
lines changed

frictionless/steps/field/field_update.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,31 @@ def transform_resource(self, resource: Resource):
7171
if new_name and resource.schema.primary_key:
7272
resource.schema.primary_key.remove(self.name)
7373
resource.schema.primary_key.append(new_name)
74+
resources = resource.package.resources if resource.package else []
75+
# update name in all the resources where it is referenced
76+
for package_resource in resources:
77+
for index, fk in enumerate(package_resource.schema.foreign_keys):
78+
fields = fk["reference"]["fields"]
79+
if isinstance(fields, list):
80+
if self.name in fk["reference"]["fields"]:
81+
package_resource.schema.foreign_keys[index]["reference"][
82+
"fields"
83+
].remove(self.name)
84+
package_resource.schema.foreign_keys[index]["reference"][
85+
"fields"
86+
].append(new_name)
87+
else:
88+
package_resource.schema.foreign_keys[index]["reference"][
89+
"fields"
90+
] = new_name
91+
92+
package_resource.schema.foreign_keys = (
93+
package_resource.schema.foreign_keys
94+
)
95+
if resource.package:
96+
resource.package.metadata_descriptor_initial = (
97+
resource.package.to_descriptor()
98+
)
7499

75100
# Metadata
76101

frictionless/steps/resource/resource_update.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,23 @@ def transform_resource(self, resource: Resource):
5151
options = helpers.create_options(self.descriptor)
5252
for name, value in options.items():
5353
setattr(resource, name, value)
54+
resources = resource.package.resources if resource.package else []
55+
new_name = options.get("name")
56+
if new_name and new_name != self.name:
57+
# update name in all the resources where it is referenced
58+
for package_resource in resources:
59+
for index, fk in enumerate(package_resource.schema.foreign_keys):
60+
if fk["reference"]["resource"] == self.name:
61+
package_resource.schema.foreign_keys[index]["reference"][
62+
"resource"
63+
] = new_name
64+
package_resource.schema.foreign_keys = (
65+
package_resource.schema.foreign_keys
66+
)
67+
if resource.package:
68+
resource.package.metadata_descriptor_initial = (
69+
resource.package.to_descriptor()
70+
)
5471

5572
# Metadata
5673

tests/steps/field/test_field_update.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from frictionless import Pipeline, steps
1+
from frictionless import Pipeline, steps, transform
2+
from frictionless.package.package import Package
23
from frictionless.resources import TableResource
34
from frictionless.schema.schema import Schema
45

@@ -92,3 +93,54 @@ def test_step_field_update_field_name_with_primary_key():
9293
)
9394
target = source.transform(pipeline)
9495
assert target.schema.primary_key == ["pkey"]
96+
97+
98+
def test_step_field_update_referenced_as_foreign_key():
99+
resource1 = TableResource(name="resource1", path="data/transform.csv")
100+
resource2 = TableResource(name="resource2")
101+
resource1.schema = Schema.from_descriptor(
102+
{
103+
"fields": [
104+
{"name": "id", "type": "integer"},
105+
{"name": "name", "type": "string"},
106+
{"name": "population", "type": "integer"},
107+
],
108+
"primaryKey": ["id"],
109+
}
110+
)
111+
resource2.schema = Schema.from_descriptor(
112+
{
113+
"fields": [
114+
{"name": "id", "type": "integer"},
115+
{"name": "address", "type": "string"},
116+
{"name": "country_name", "type": "integer"},
117+
],
118+
"primaryKey": ["id"],
119+
"foreignKeys": [
120+
{
121+
"fields": ["country_name"],
122+
"reference": {"fields": ["id"], "resource": "resource1"},
123+
}
124+
],
125+
}
126+
)
127+
package = Package(name="test-package", resources=[resource1, resource2])
128+
transform(
129+
package,
130+
steps=[
131+
steps.resource_transform(
132+
name="resource1",
133+
steps=[steps.field_update(name="id", descriptor={"name": "pkey"})],
134+
)
135+
],
136+
)
137+
assert (
138+
package.get_resource("resource1").validate().flatten(["title", "message"]) == []
139+
)
140+
assert package.get_resource("resource1").schema.primary_key == ["pkey"]
141+
assert package.get_resource("resource2").schema.foreign_keys == [
142+
{
143+
"fields": ["country_name"],
144+
"reference": {"fields": ["pkey"], "resource": "resource1"},
145+
}
146+
]

tests/steps/resource/test_resource_update.py

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from frictionless import Package, Pipeline, steps
1+
from frictionless import Package, Pipeline, Schema, steps, transform
22
from frictionless.resources import TableResource
33

44
# General
@@ -38,3 +38,56 @@ def test_step_resource_update_standalone_issue_1351():
3838
)
3939
target = source.transform(pipeline)
4040
assert target.title == "New title"
41+
42+
43+
def test_step_resource_update_referenced_as_foreign_key():
44+
resource1 = TableResource(name="resource1", path="data/transform.csv")
45+
resource2 = TableResource(name="resource2")
46+
resource1.schema = Schema.from_descriptor(
47+
{
48+
"fields": [
49+
{"name": "id", "type": "integer"},
50+
{"name": "name", "type": "string"},
51+
{"name": "population", "type": "integer"},
52+
],
53+
"primaryKey": ["id"],
54+
}
55+
)
56+
resource2.schema = Schema.from_descriptor(
57+
{
58+
"fields": [
59+
{"name": "id", "type": "integer"},
60+
{"name": "address", "type": "string"},
61+
{"name": "country_name", "type": "integer"},
62+
],
63+
"primaryKey": ["id"],
64+
"foreignKeys": [
65+
{
66+
"fields": ["country_name"],
67+
"reference": {"fields": ["id"], "resource": "resource1"},
68+
}
69+
],
70+
}
71+
)
72+
package = Package(name="test-package", resources=[resource1, resource2])
73+
transform(
74+
package,
75+
steps=[
76+
steps.resource_transform(
77+
name="resource1",
78+
steps=[
79+
steps.resource_update(
80+
name="resource1", descriptor={"name": "first-resource"}
81+
)
82+
],
83+
)
84+
],
85+
)
86+
assert (
87+
package.get_resource("first-resource").validate().flatten(["title", "message"])
88+
== []
89+
)
90+
assert (
91+
package.get_resource("resource2").schema.foreign_keys[0]["reference"]["resource"]
92+
== "first-resource"
93+
)

0 commit comments

Comments
 (0)