From 4b47f1d530d2638b9935c2ae4c869929b10d2132 Mon Sep 17 00:00:00 2001 From: vkrasnovyd Date: Sat, 27 Jun 2026 11:58:46 +0200 Subject: [PATCH 1/4] Check reference in generator more precisely --- dev/src/helper_get_names.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index f24ef131..061e6633 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -700,7 +700,9 @@ def generate_field_or_sql_decision( primary: bool | str | None error = "" - if own_card in foreign_card_replacement_list: + if own_card in foreign_card_replacement_list and ( + "r" not in foreign_card or own_card[0] != foreign_card[0] + ): foreign_card = "" state, primary = decision_list.get((own_card, foreign_card), (None, None)) From 88f27e631e3253cb8a873ac147e1e3d61c8b52e1 Mon Sep 17 00:00:00 2001 From: vkrasnovyd Date: Sat, 27 Jun 2026 12:26:42 +0200 Subject: [PATCH 2/4] Check reference in validator --- dev/src/validate.py | 62 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/dev/src/validate.py b/dev/src/validate.py index 2d275c06..8ade2f0a 100644 --- a/dev/src/validate.py +++ b/dev/src/validate.py @@ -324,9 +324,11 @@ def check_field( f"invalid value for 'on_delete' for {collectionfield}" ) valid_attributes.append("equal_fields") + if type != "generic-relation-list": + valid_attributes.append("reference") if nested and type in ("relation", "relation-list"): valid_attributes.append("enum") - valid_attributes.extend(("reference", "deferred", "sql")) + valid_attributes.extend(("deferred", "sql")) if field.get("sql"): valid_attributes.append("log_triggers") self.check_log_triggers(collectionfield, field) @@ -586,15 +588,10 @@ def check_reverse( to_collectionfield, to_field, ) - if all( - [ - "reference" in field and field["type"] == "relation" - for field in [to_field, from_field] - ] + if reference_error := self.check_reference( + from_collectionfield, from_field, to_collectionfield, to_field ): - self.errors.append( - f"The relation fields {from_collectionfield} and {to_collectionfield} both have reference set." - ) + self.errors.append(reference_error) to = to_field["to"] if isinstance(to, str): @@ -705,6 +702,53 @@ def split_collectionfield(self, collectionfield: str) -> tuple[str, str]: parts = collectionfield.split(KEYSEPARATOR) return parts[0], parts[1] + def check_reference( + self, + from_collectionfield: str, + from_field: dict[str, Any], + to_collectionfield: str, + to_field: dict[str, Any], + ) -> str | None: + reference_in_fields = ["reference" in field for field in [to_field, from_field]] + if ( + from_field["type"] == to_field["type"] + and from_field["type"] in ["relation", "relation-list"] + and all(reference_in_fields) + ): + return f"The relation fields {from_collectionfield} and {to_collectionfield} both have 'reference' set." + + if from_field["type"] == to_field["type"] == "relation" and not any( + reference_in_fields + ): + return f"One of the relation fields {from_collectionfield} and {to_collectionfield} must have 'reference' set." + + allowed, required = self.get_reference_allowed_required(from_field, to_field) + if not allowed and from_field.get("reference"): + return f"Relational collectionfield {from_collectionfield} can not have 'reference' attribute." + + if required and not from_field.get("reference"): + return f"Relational collectionfield {from_collectionfield} must have 'reference' attribute." + + return None + + def get_reference_allowed_required( + self, from_field: dict[str, Any], to_field: dict[str, Any] + ) -> tuple[bool, bool]: + if from_field["type"] == to_field["type"] == "relation": + return (True, False) + if from_field["type"] == "relation-list" and to_field["type"] in [ + "relation", + "generic-relation", + ]: + return (True, False) + if (from_field["type"], to_field["type"]) in ( + ("generic-relation", "relation-list"), + ("generic-relation", "relation"), + ("relation", "relation-list"), + ): + return (True, True) + return (False, False) + def main() -> int: dirs = sys.argv[1:] From 0e7470257953616168d4901b41cebe97869436f5 Mon Sep 17 00:00:00 2001 From: vkrasnovyd Date: Wed, 8 Jul 2026 18:41:45 +0200 Subject: [PATCH 3/4] Rename variables --- dev/src/validate.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/dev/src/validate.py b/dev/src/validate.py index 8ade2f0a..97e8bfbc 100644 --- a/dev/src/validate.py +++ b/dev/src/validate.py @@ -722,11 +722,13 @@ def check_reference( ): return f"One of the relation fields {from_collectionfield} and {to_collectionfield} must have 'reference' set." - allowed, required = self.get_reference_allowed_required(from_field, to_field) - if not allowed and from_field.get("reference"): + reference_allowed, reference_required = self.get_reference_allowed_required( + from_field, to_field + ) + if not reference_allowed and from_field.get("reference"): return f"Relational collectionfield {from_collectionfield} can not have 'reference' attribute." - if required and not from_field.get("reference"): + if reference_required and not from_field.get("reference"): return f"Relational collectionfield {from_collectionfield} must have 'reference' attribute." return None From 212aea92f0dacfa492a594d9f010a85acc7b2e13 Mon Sep 17 00:00:00 2001 From: vkrasnovyd Date: Thu, 9 Jul 2026 09:54:13 +0200 Subject: [PATCH 4/4] Check reference value --- dev/src/validate.py | 55 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/dev/src/validate.py b/dev/src/validate.py index 97e8bfbc..feb2cb1f 100644 --- a/dev/src/validate.py +++ b/dev/src/validate.py @@ -476,6 +476,9 @@ def check_relation( error = self.check_reverse(collectionfield, field, cf) if error: return error + self.check_reference_list( + collectionfield, field, [c.split(KEYSEPARATOR)[0] for c in to] + ) else: to_field = to["field"] if not FIELD_REGEX.match(to_field): @@ -492,6 +495,7 @@ def check_relation( ) if error: return error + self.check_reference_list(collectionfield, field, to["collections"]) return None def setify_equal_fields(self, field_def: dict[str, Any]) -> set[str]: @@ -709,6 +713,7 @@ def check_reference( to_collectionfield: str, to_field: dict[str, Any], ) -> str | None: + # Check attribute presence reference_in_fields = ["reference" in field for field in [to_field, from_field]] if ( from_field["type"] == to_field["type"] @@ -725,14 +730,58 @@ def check_reference( reference_allowed, reference_required = self.get_reference_allowed_required( from_field, to_field ) - if not reference_allowed and from_field.get("reference"): + reference = from_field.get("reference") + if not reference_allowed and reference: return f"Relational collectionfield {from_collectionfield} can not have 'reference' attribute." + if not reference: + if reference_required: + return f"Relational collectionfield {from_collectionfield} must have 'reference' attribute." + return None + + # Check value + if from_field["type"] in ["relation", "relation-list"]: + if not isinstance(reference, str) or reference not in self.models: + return f"The collection '{reference}' in 'reference' of {from_collectionfield} is not a valid collection." + + if reference != ( + to_collection := to_collectionfield.split(KEYSEPARATOR)[0] + ): + return f"'reference' of {from_collectionfield} must match the collection of 'to': {to_collection}." - if reference_required and not from_field.get("reference"): - return f"Relational collectionfield {from_collectionfield} must have 'reference' attribute." + else: + assert isinstance( + reference, list + ), f"Reference of {from_collectionfield} must be a list of valid collection names." + if ( + to_collection := to_collectionfield.split(KEYSEPARATOR)[0] + ) not in reference: + return f"'reference' of {from_collectionfield} is missing a collection '{to_collection}' from 'to'." return None + def check_reference_list( + self, + from_collectionfield: str, + from_field: dict[str, Any], + to_collections: list[Any], + ) -> None: + if not (reference := from_field.get("reference")): + return + + if invalid := [value for value in reference if value not in self.models]: + self.errors.append( + f"'reference' of {from_collectionfield} contains values that are not valid collections: {invalid}" + ) + + if not_in_to := [ + value + for value in reference + if value not in to_collections and value not in invalid + ]: + self.errors.append( + f"'reference' of {from_collectionfield} contains collections not present in 'to': {not_in_to}" + ) + def get_reference_allowed_required( self, from_field: dict[str, Any], to_field: dict[str, Any] ) -> tuple[bool, bool]: