CheckColumns Constraint (#730)

* feat: add check columns class

* feat: write document of check columns part

* test: write a test for check columns constraints

* fix: debuging test exception raise mysql

* fix: set pragma no cover to ignore cov

* fix: ignore pytest raise in python 3.x not 10

* feat: set constraint name for check columns

* refactor: support index and check overwrites

* fix: debuging check constraint arguments

* fix: debug coverage all code tests

* fix: pass the map of type constraint to counter

* refactor: edit check name replace sapce underline

* refactor: write new function copy constraints

* test: write test for invalid constraint type

* fix: debug text cluase replaced names

* fix: set pragma no cover for result returned

* refactor: no coverage for main if statement

* perf: change get constraint copy func code

* fix: fix bug in mypy typing check
This commit is contained in:
Sepehr Bazyar
2022-07-14 12:35:30 +04:30
committed by GitHub
parent 3ad563e5dd
commit b6e057c303
9 changed files with 160 additions and 10 deletions

View File

@ -298,7 +298,7 @@ def populate_meta_sqlalchemy_table_if_required(meta: "ModelMeta") -> None:
def set_constraint_names(meta: "ModelMeta") -> None:
"""
Populates the names on IndexColumn and UniqueColumns constraints.
Populates the names on IndexColumns and UniqueColumns and CheckColumns constraints.
:param meta: Meta class of the Model without sqlalchemy table constructed
:type meta: Model class Meta
@ -317,6 +317,9 @@ def set_constraint_names(meta: "ModelMeta") -> None:
f"ix_{meta.tablename}_"
f'{"_".join([col for col in constraint._pending_colargs])}'
)
elif isinstance(constraint, sqlalchemy.CheckConstraint) and not constraint.name:
sql_condition: str = str(constraint.sqltext).replace(" ", "_")
constraint.name = f"check_{meta.tablename}_{sql_condition}"
def update_column_definition(

View File

@ -9,6 +9,7 @@ from typing import (
Type,
Union,
cast,
Callable,
)
import databases
@ -18,6 +19,7 @@ from sqlalchemy.sql.schema import ColumnCollectionConstraint
import ormar # noqa I100
import ormar.fields.constraints
from ormar.fields.constraints import UniqueColumns, IndexColumns, CheckColumns
from ormar import ModelDefinitionError # noqa I100
from ormar.exceptions import ModelError
from ormar.fields import BaseField
@ -186,7 +188,7 @@ def verify_constraint_names(
for column_set in constraints_columns:
if any(x not in old_aliases.values() for x in column_set):
raise ModelDefinitionError(
f"Unique columns constraint "
f"Column constraints "
f"{column_set} "
f"has column names "
f"that are not in the model fields."
@ -195,6 +197,33 @@ def verify_constraint_names(
)
def get_constraint_copy(
constraint: ColumnCollectionConstraint,
) -> Union[UniqueColumns, IndexColumns, CheckColumns]:
"""
Copy the constraint and unpacking it's values
:raises ValueError: if non subclass of ColumnCollectionConstraint
:param value: an instance of the ColumnCollectionConstraint class
:type value: Instance of ColumnCollectionConstraint child
:return: copy ColumnCollectionConstraint ormar constraints
:rtype: Union[UniqueColumns, IndexColumns, CheckColumns]
"""
constraints = {
sqlalchemy.UniqueConstraint: lambda x: UniqueColumns(*x._pending_colargs),
sqlalchemy.Index: lambda x: IndexColumns(*x._pending_colargs),
sqlalchemy.CheckConstraint: lambda x: CheckColumns(x.sqltext),
}
checks = (key if isinstance(constraint, key) else None for key in constraints)
target_class = next((target for target in checks if target is not None), None)
constructor: Optional[Callable] = constraints.get(target_class)
if not constructor:
raise ValueError(f"{constraint} must be a ColumnCollectionMixin!")
return constructor(constraint)
def update_attrs_from_base_meta( # noqa: CCR001
base_class: "Model", attrs: Dict, model_fields: Dict
) -> None:
@ -222,10 +251,7 @@ def update_attrs_from_base_meta( # noqa: CCR001
model_fields=model_fields,
parent_value=parent_value,
)
parent_value = [
ormar.fields.constraints.UniqueColumns(*x._pending_colargs)
for x in parent_value
]
parent_value = [get_constraint_copy(value) for value in parent_value]
if isinstance(current_value, list):
current_value.extend(parent_value)
else: