Skip to content

Commit

Permalink
feat: create a reverse relationship with TupleField
Browse files Browse the repository at this point in the history
  • Loading branch information
pity7736 committed May 30, 2023
1 parent ea73553 commit 72eb582
Show file tree
Hide file tree
Showing 10 changed files with 1,046 additions and 500 deletions.
1 change: 1 addition & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@

# The full version, including alpha/beta/rc tags
release = VERSION
version = VERSION


# -- General configuration ---------------------------------------------------
Expand Down
590 changes: 427 additions & 163 deletions nyoibo/entities/entity.c

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions nyoibo/entities/entity.pyx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from nyoibo cimport fields
from .meta_entity import MetaEntity
from ..utils import camel_to_snake_case


class Entity(metaclass=MetaEntity):
Expand Down Expand Up @@ -32,4 +33,8 @@ class Entity(metaclass=MetaEntity):
if issubclass(type(current_value), fields.Field):
current_value = None
value = field.parse(current_value or value)

setattr(self, key, value)
if isinstance(field, fields.TupleField) and field.reverse_relationship:
for subfield in value:
setattr(subfield, f'_{camel_to_snake_case(self.__class__.__name__)}', self)
12 changes: 11 additions & 1 deletion nyoibo/entities/meta_entity.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from typing import Dict, Any

from nyoibo.exceptions import PrivateFieldError
from nyoibo.fields import Field
from nyoibo.fields import Field, TupleField

from nyoibo.utils import camel_to_snake_case


def create_getter(attr):
Expand Down Expand Up @@ -35,6 +37,14 @@ def __new__(mcs, name, bases, namespace: Dict[str, Any]):
value,
namespace
)
if isinstance(value, TupleField) and \
value.reverse_relationship and value.private is False:
class_name_snake_case = camel_to_snake_case(name)
setattr(
value.of,
class_name_snake_case,
property(create_getter(f'_{class_name_snake_case}'))
)

namespace.update(getters_setters)
namespace['_fields'] = fields
Expand Down
850 changes: 516 additions & 334 deletions nyoibo/fields.c

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions nyoibo/fields.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ cdef class LinkField(Field):

cdef class TupleField(Field):
cdef readonly of
cdef readonly reverse_relationship
12 changes: 11 additions & 1 deletion nyoibo/fields.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -279,14 +279,20 @@ cdef class TupleField(Field):
Args:
of: Type of items. All items going to be cast to ``of`` type.
reverse_relationship (bool): create a reverse relation with ``of``.
Raises:
ValueError: if of is ``None`` and ``reverse_relationship`` is True
"""

_internal_type = tuple

def __init__(self, of=None, *args, **kwargs):
def __init__(self, of=None, reverse_relationship=False, *args, **kwargs):
if reverse_relationship and of is None:
raise ValueError('to make a reverse relationship, `of` parameter must to be set')
super().__init__(*args, **kwargs)
self.of = of
self.reverse_relationship = reverse_relationship

cdef _parse(self, value):
if value and self.of:
Expand All @@ -312,6 +318,10 @@ cdef class ListField(TupleField):
Args:
of: Type of items. All items going to be cast to ``of`` type.
reverse_relationship (bool): create a reverse relation with ``of``.
Raises:
ValueError: if of is ``None`` and ``reverse_relationship`` is True
"""

_internal_type = list
6 changes: 6 additions & 0 deletions nyoibo/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import re


def camel_to_snake_case(value):
value = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', value)
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', value).lower()
61 changes: 60 additions & 1 deletion tests/test_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ def __eq__(self, other):

class Owner(Entity):
_name = fields.StrField()
_licenses = fields.TupleField(of=License)
_licenses = fields.TupleField(of=License, reverse_relationship=True)

data = {
'name': 'test owner name',
Expand All @@ -364,3 +364,62 @@ class Owner(Entity):
License(category='A1'),
License(category='A2')
)
assert owner.licenses[0].owner == owner
assert owner.licenses[1].owner == owner


def test_reverse_relationship():
class License(Entity):
_category = fields.StrField()

def __eq__(self, other):
return self._category == other._category

class OwnerModel(Entity):
_name = fields.StrField()
_licenses = fields.TupleField(of=License, reverse_relationship=True)

def __eq__(self, other):
return self._name == other._name

owner = OwnerModel(
name='test name',
licenses=(
License(category='A1'),
License(category='A2')
)
)

assert owner.licenses[0].owner_model == owner
assert owner.licenses[1].owner_model == owner


def test_reverse_relationship_with_private_field():
class License(Entity):
_category = fields.StrField()

def compare_owner(self, owner):
return self._owner == owner

def __eq__(self, other):
return self._category == other._category

class Owner(Entity):
_name = fields.StrField()
_licenses = fields.TupleField(
of=License,
reverse_relationship=True,
private=True
)

def __eq__(self, other):
return self._name == other._name
lic = License(category='A1')
owner = Owner(
name='test name',
licenses=(lic,)
)

assert lic.compare_owner(owner) is True
with raises(AttributeError):
lic.owner
8 changes: 8 additions & 0 deletions tests/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,3 +373,11 @@ def test_parse_list_field():
def test_parse_list_field_with_type():
field = fields.ListField(of=int)
assert field.parse((1, '2', 3, 4.0)) == [1, 2, 3, 4]


def test_tuple_field_with_reverse_relationship():
with raises(ValueError) as e:
fields.TupleField(reverse_relationship=True)

assert str(e.value) == 'to make a reverse relationship, ' \
'`of` parameter must to be set'

0 comments on commit 72eb582

Please sign in to comment.