Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion dev/src/helper_get_names.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
113 changes: 104 additions & 9 deletions dev/src/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Comment thread
luisa-beerboom marked this conversation as resolved.
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)
Expand Down Expand Up @@ -474,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):
Expand All @@ -490,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]:
Expand Down Expand Up @@ -586,15 +592,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):
Expand Down Expand Up @@ -705,6 +706,100 @@ 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:
# Check attribute presence
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."

reference_allowed, reference_required = self.get_reference_allowed_required(
from_field, to_field
)
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}."

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]:
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"),
):
Comment thread
luisa-beerboom marked this conversation as resolved.
return (True, True)
return (False, False)


def main() -> int:
dirs = sys.argv[1:]
Expand Down