Skip to content

Commit

Permalink
Add tests to classic foreign key
Browse files Browse the repository at this point in the history
  • Loading branch information
tarsil committed Jul 31, 2023
1 parent 070d09d commit 0518558
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 77 deletions.
29 changes: 24 additions & 5 deletions edgy/core/db/base.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import decimal
from typing import Any, Optional, Pattern, Sequence, Union

import sqlalchemy
from pydantic.fields import FieldInfo

from edgy.core.db.constraints.base import Constraint
from edgy.core.connection.registry import Registry
from edgy.types import Undefined


Expand Down Expand Up @@ -62,9 +63,10 @@ def __init__(
"multiple_of", None
)
self.through: Any = kwargs.pop("through", None)

# Constraints
self.contraints: Constraint = kwargs.pop("constraints", None)
self.server_default: Any = kwargs.pop("server_default", None)
self.server_onupdate: Any = kwargs.pop("server_onupdate", None)
self.registry: Registry = kwargs.pop("registry", None)
self.comment = kwargs.get("comment", None)

for name, value in kwargs.items():
setattr(self, name, value)
Expand Down Expand Up @@ -118,7 +120,21 @@ def get_column(self, name: str) -> Any:
"""
Returns the column type of the field being declared.
"""
return self._type
column_type = self.get_column_type()
constraints = self.get_constraints()
column = sqlalchemy.Column(
name,
column_type,
*constraints,
primary_key=self.primary_key,
nullable=self.null and not self.primary_key,
index=self.index,
unique=self.unique,
default=self.default,
comment=self.comment,
server_default=self.server_default,
server_onupdate=self.server_onupdate,
)

def expand_relationship(self, value: Any, child: Any, to_register: bool = True) -> Any:
"""
Expand All @@ -130,3 +146,6 @@ def expand_relationship(self, value: Any, child: Any, to_register: bool = True)
def get_related_name(self) -> str:
"""Returns the related name used for reverse relations"""
return ""

def get_constraints(self) -> Any:
return []
Empty file.
44 changes: 0 additions & 44 deletions edgy/core/db/constraints/base.py

This file was deleted.

8 changes: 0 additions & 8 deletions edgy/core/db/fields/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import sqlalchemy

from edgy.core.db.base import BaseField
from edgy.core.db.constraints.base import Constraint
from edgy.exceptions import FieldDefinitionError

CLASS_DEFAULTS = ["cls", "__class__", "kwargs"]
Expand All @@ -32,7 +31,6 @@ def __new__(cls, *args: Any, **kwargs: Any) -> BaseField: # type: ignore
index: bool = kwargs.pop("index", False)
name: str = kwargs.pop("name", None)
choices: Set[Any] = set(kwargs.pop("choices", []))
constraint: Constraint = kwargs.pop("constraint", None)
comment: str = kwargs.pop("comment", None)
owner = kwargs.pop("owner", None)
server_default = kwargs.pop("server_default", None)
Expand All @@ -50,7 +48,6 @@ def __new__(cls, *args: Any, **kwargs: Any) -> BaseField: # type: ignore
unique=unique,
autoincrement=autoincrement,
choices=choices,
constraints=constraint,
comment=comment,
owner=owner,
server_default=server_default,
Expand All @@ -73,11 +70,6 @@ def get_column_type(cls, **kwargs: Any) -> Any:
"""Returns the propery column type for the field"""
return None

@classmethod
def build_constraint(cls) -> Union[Constraint, None]:
"""Builds the constraints for the field"""
return None


class CharField(FieldFactory, str):
"""String field representation that constructs the Field class and populates the values"""
Expand Down
50 changes: 31 additions & 19 deletions edgy/core/db/fields/foreign_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
import sqlalchemy

import edgy
from edgy.core.connection.registry import Registry
from edgy.core.db.base import BaseField
from edgy.core.db.constants import CASCADE, RESTRICT, SET_NULL
from edgy.core.db.constraints.base import Constraint
from edgy.core.terminal import Print
from edgy.exceptions import FieldDefinitionError

Expand All @@ -25,16 +25,17 @@ class ForeignKeyFieldFactory:
def __new__(cls, *args: Any, **kwargs: Any) -> BaseField: # type: ignore
cls.validate(**kwargs)

to: Any = kwargs.get("to", None)
to: Any = kwargs.pop("to", None)
null: bool = kwargs.pop("null", False)
on_update: str = kwargs.pop("on_update", CASCADE)
on_delete: str = kwargs.pop("on_delete", RESTRICT)
related_name: str = kwargs.pop("related_name", None)
comment: str = kwargs.pop("comment", None)
through: Any = kwargs.pop("through", None)
owner = kwargs.pop("owner", None)
server_default = kwargs.pop("server_default", None)
server_onupdate = kwargs.pop("server_onupdate", None)
owner: Any = kwargs.pop("owner", None)
server_default: Any = kwargs.pop("server_default", None)
server_onupdate: Any = kwargs.pop("server_onupdate", None)
registry: Registry = kwargs.pop("registry", None)
field_type = cls._type

namespace = dict(
Expand All @@ -50,6 +51,7 @@ def __new__(cls, *args: Any, **kwargs: Any) -> BaseField: # type: ignore
server_default=server_default,
server_onupdate=server_onupdate,
through=through,
registry=registry,
**kwargs,
)
Field = type(cls.__name__, cls._bases, {})
Expand All @@ -68,40 +70,42 @@ def get_column_type(cls, **kwargs: Any) -> Any:
"""Returns the propery column type for the field"""
return None

@classmethod
def build_constraint(cls) -> Union[Constraint, None]:
"""Builds the constraints for the field"""
return None


class ForeignKey(ForeignKeyFieldFactory):
_type = Any

def __new__( # type: ignore
cls,
*,
to: Any,
to: "Model",
null: bool = False,
on_update: Optional[str] = CASCADE,
on_delete: Optional[str] = RESTRICT,
related_name: Optional[str] = None,
**kwargs: Any,
) -> BaseField:
assert on_delete is not None, "on_delete must not be null"

if on_delete == SET_NULL and not null:
raise FieldDefinitionError("When SET_NULL is enabled, null must be True.")

if on_update and (on_update == SET_NULL and not null):
raise FieldDefinitionError("When SET_NULL is enabled, null must be True.")

kwargs = {
**kwargs,
**{key: value for key, value in locals().items() if key not in CLASS_DEFAULTS},
}

return super().__new__(cls, **kwargs)

@classmethod
def validate(cls, **kwargs: Any) -> None:
on_delete = kwargs.get("on_delete", None)
on_update = kwargs.get("on_update", None)
null = kwargs.get("null")

if on_delete is None:
raise FieldDefinitionError("on_delete must not be null")

if on_delete == SET_NULL and not null:
raise FieldDefinitionError("When SET_NULL is enabled, null must be True.")

if on_update and (on_update == SET_NULL and not null):
raise FieldDefinitionError("When SET_NULL is enabled, null must be True.")

@property
def target(self) -> Any:
"""
Expand Down Expand Up @@ -130,6 +134,14 @@ def get_column(self, name: str) -> Any:
]
return sqlalchemy.Column(name, column_type, *constraints, nullable=self.null)

def get_related_name(self) -> str:
"""
Returns the name of the related name of the current relationship between the to and target.
:return: Name of the related_name attribute field.
"""
return self.related_name

def expand_relationship(self, value: Any) -> Any:
target = self.target
if isinstance(value, target):
Expand Down
15 changes: 14 additions & 1 deletion edgy/core/db/models/metaclasses.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,34 @@
import copy
import inspect
from typing import (
TYPE_CHECKING,
Any,
Dict,
List,
Optional,
Sequence,
Set,
Type,
TypeVar,
Union,
cast,
)

import sqlalchemy

from edgy.conf import settings
from edgy.core.connection.database import Database
from edgy.core.connection.registry import Registry
from edgy.core.db import fields, models
from edgy.core.db.datastructures import Index, UniqueConstraint
from edgy.core.db.fields.base import BaseField
from edgy.core.db.models.managers import Manager
from edgy.core.db.relationships.related_field import RelatedField
from edgy.core.db.relationships.relation import Relation
from edgy.exceptions import ForeignKeyBadConfigured, ImproperlyConfigured

if TYPE_CHECKING:
from edgy.core.db.models import Model
from edgy.core.db.models import Model, ReflectModel


class MetaInfo:
Expand Down
File renamed without changes.
32 changes: 32 additions & 0 deletions tests/fields/test_foreignkeys.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import pytest

import edgy
from edgy import ForeignKey, Model, OneToOne, OneToOneField
from edgy.exceptions import FieldDefinitionError


class MyModel(Model):
""""""


@pytest.mark.parametrize("model", [ForeignKey, OneToOne, OneToOneField])
def test_can_create_foreign_key(model):
fk = model(to=MyModel)

assert fk is not None
assert fk.to == MyModel


def test_raise_error_on_delete_fk():
with pytest.raises(FieldDefinitionError):
ForeignKey(to=MyModel, on_delete=None)


def test_raise_error_on_delete_null():
with pytest.raises(FieldDefinitionError):
ForeignKey(to=MyModel, on_delete=edgy.SET_NULL)


def test_raise_error_on_update_null():
with pytest.raises(FieldDefinitionError):
ForeignKey(to=MyModel, on_update=edgy.SET_NULL)
File renamed without changes.

0 comments on commit 0518558

Please sign in to comment.