Referential Actions Enum Class (#735)

* feat: add action enum class to referential actions

* feat: write validation func for action name string

* test: write test for validation referential action

* fix: backend database running for action test

* fix: set the string type of value enum class

* fix: debuging return statement type for validation

* fix: return non empty for empty action

* refactor: change in line return if statement

* fix: add iterate method in read document md

* fix: update foreign key docstring types

* docs: write documention of refernal actions

* docs: complete referential actions descriptions

* refactor: rename and reposition referential action

* refactor: change validate referential action func

* test: add assert check for really deleted rows

* fix: debug error problem in renamed enum class

* fix: apply black formatted codes

* docs: update the document for referential actions

* docs: added note for server default argument

Co-authored-by: collerek <collerek@gmail.com>
This commit is contained in:
Sepehr Bazyar
2022-07-22 17:35:37 +04:30
committed by GitHub
parent bbc214daf2
commit 991d4a2a2c
7 changed files with 160 additions and 8 deletions

View File

@ -57,7 +57,6 @@ from ormar.fields import (
Float,
ForeignKey,
ForeignKeyField,
IndexColumns,
Integer,
JSON,
LargeBinary,
@ -70,7 +69,9 @@ from ormar.fields import (
Time,
UUID,
UniqueColumns,
IndexColumns,
CheckColumns,
ReferentialAction,
) # noqa: I100
from ormar.models import ExcludableItems, Extra, Model
from ormar.models.metaclass import ModelMeta
@ -103,6 +104,7 @@ __all__ = [
"Float",
"ManyToMany",
"Model",
"Action",
"ModelDefinitionError",
"MultipleMatches",
"NoMatch",
@ -114,6 +116,7 @@ __all__ = [
"UniqueColumns",
"IndexColumns",
"CheckColumns",
"ReferentialAction",
"QuerySetProtocol",
"RelationProtocol",
"ModelMeta",

View File

@ -28,6 +28,7 @@ from ormar.fields.model_fields import (
from ormar.fields.parsers import DECODERS_MAP, ENCODERS_MAP, SQL_ENCODERS_MAP
from ormar.fields.sqlalchemy_encrypted import EncryptBackend, EncryptBackends
from ormar.fields.through_field import Through, ThroughField
from ormar.fields.referential_actions import ReferentialAction
__all__ = [
"Decimal",
@ -38,7 +39,6 @@ __all__ = [
"DateTime",
"String",
"JSON",
"IndexColumns",
"Integer",
"Text",
"Float",
@ -59,4 +59,7 @@ __all__ = [
"SQL_ENCODERS_MAP",
"LargeBinary",
"UniqueColumns",
"IndexColumns",
"CheckColumns",
"ReferentialAction",
]

View File

@ -21,6 +21,7 @@ from pydantic.typing import ForwardRef, evaluate_forwardref
import ormar # noqa I101
from ormar.exceptions import ModelDefinitionError, RelationshipInstanceError
from ormar.fields.referential_actions import ReferentialAction
from ormar.fields.base import BaseField
if TYPE_CHECKING: # pragma no cover
@ -159,6 +160,27 @@ def validate_not_allowed_fields(kwargs: Dict) -> None:
)
def validate_referential_action(
action: Optional[Union[ReferentialAction, str]],
) -> Optional[str]:
"""
Validation `onupdate` and `ondelete` action cast to a string value
:raises ModelDefinitionError: if action is a not valid name string value
:param action: referential action attribute or name string
:type action: Optional[Union[ReferentialAction, str]]
:rtype: Optional[str]
"""
if action is not None and not isinstance(action, ReferentialAction):
try:
action = ReferentialAction(action.upper())
except (ValueError, AttributeError):
raise ModelDefinitionError(f"{action} ReferentialAction not supported.")
return action.value if action is not None else None
@dataclass
class ForeignKeyConstraint:
"""
@ -190,8 +212,8 @@ def ForeignKey( # type: ignore # noqa CFQ002
nullable: bool = True,
related_name: str = None,
virtual: bool = False,
onupdate: str = None,
ondelete: str = None,
onupdate: Union[ReferentialAction, str] = None,
ondelete: Union[ReferentialAction, str] = None,
**kwargs: Any,
) -> "T":
"""
@ -215,16 +237,19 @@ def ForeignKey( # type: ignore # noqa CFQ002
:type virtual: bool
:param onupdate: parameter passed to sqlalchemy.ForeignKey.
How to treat child rows on update of parent (the one where FK is defined) model.
:type onupdate: str
:type onupdate: Union[ReferentialAction, str]
:param ondelete: parameter passed to sqlalchemy.ForeignKey.
How to treat child rows on delete of parent (the one where FK is defined) model.
:type ondelete: str
:type ondelete: Union[ReferentialAction, str]
:param kwargs: all other args to be populated by BaseField
:type kwargs: Any
:return: ormar ForeignKeyField with relation to selected model
:rtype: ForeignKeyField
"""
onupdate = validate_referential_action(action=onupdate)
ondelete = validate_referential_action(action=ondelete)
owner = kwargs.pop("owner", None)
self_reference = kwargs.pop("self_reference", False)

View File

@ -0,0 +1,26 @@
"""
Gathers all referential actions by ormar.
"""
from enum import Enum
class ReferentialAction(Enum):
"""
Because the database management system(DBMS) enforces referential constraints,
it must ensure data integrity
if rows in a referenced table are to be deleted (or updated).
If dependent rows in referencing tables still exist,
those references have to be considered.
SQL specifies 5 different referential actions
that shall take place in such occurrences.
"""
CASCADE: str = "CASCADE"
RESTRICT: str = "RESTRICT"
SET_NULL: str = "SET NULL"
SET_DEFAULT: str = "SET DEFAULT"
DO_NOTHING: str = "NO ACTION"