From 7663af5fca40c5586764f813479cf5aa5093360a Mon Sep 17 00:00:00 2001 From: Robin Schyboll Date: Tue, 23 Nov 2021 18:10:24 +0000 Subject: [PATCH 1/5] Added support to create Generic models Removed __getitem__ from ModelMeta and replaced it with __class_getitem__ in Model class, which checks if passes key is a class. If it is, it returns a _GenericAlias instance. If not, it makes the same action as before. --- tortoise/models.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/tortoise/models.py b/tortoise/models.py index 76d1bc625..bb1cf7932 100644 --- a/tortoise/models.py +++ b/tortoise/models.py @@ -17,6 +17,8 @@ Type, TypeVar, Union, + _GenericAlias, + overload, ) from pypika import Order, Query, Table @@ -630,9 +632,6 @@ def __search_for_field_attributes(base: Type, attrs: dict) -> None: meta.finalise_fields() return new_class - def __getitem__(cls: Type[MODEL], key: Any) -> QuerySetSingle[MODEL]: # type: ignore - return cls._getbypk(key) # type: ignore - class Model(metaclass=ModelMeta): """ @@ -663,6 +662,21 @@ def __init__(self, **kwargs: Any) -> None: else: setattr(self, key, field_object.default) + @overload + def __class_getitem__(cls: Type[MODEL], key: type) -> _GenericAlias: + pass + + @overload + def __class_getitem__(cls: Type[MODEL], key: Any) -> QuerySetSingle[MODEL]: + pass + + def __class_getitem__( + cls: Type[MODEL], key: Any + ) -> Union[QuerySetSingle[MODEL], _GenericAlias]: + if (isinstance(key, tuple) and inspect.isclass(key[0])) or inspect.isclass(key): + return _GenericAlias(cls, key) + return cls._getbypk(key) + def _set_kwargs(self, kwargs: dict) -> Set[str]: meta = self._meta From a6dd2ad49f15075463677ed4bc9500470eb0bc0b Mon Sep 17 00:00:00 2001 From: Robin Schyboll Date: Wed, 24 Nov 2021 22:59:24 +0000 Subject: [PATCH 2/5] Fixed "typing" has not attribute "_GenericAlias" Changed the __class_getitem__ method from returing directly a _GenericAliast method to it calling the __class_getitem__ from the Generic class. Also added a check, if the model inherits from Generic. It's a safety measure, so that the user has to inherit from Generic to create generic models. --- tortoise/models.py | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/tortoise/models.py b/tortoise/models.py index bb1cf7932..5d80f480e 100644 --- a/tortoise/models.py +++ b/tortoise/models.py @@ -17,8 +17,7 @@ Type, TypeVar, Union, - _GenericAlias, - overload, + Generic, ) from pypika import Order, Query, Table @@ -662,21 +661,12 @@ def __init__(self, **kwargs: Any) -> None: else: setattr(self, key, field_object.default) - @overload - def __class_getitem__(cls: Type[MODEL], key: type) -> _GenericAlias: - pass - - @overload - def __class_getitem__(cls: Type[MODEL], key: Any) -> QuerySetSingle[MODEL]: - pass - - def __class_getitem__( - cls: Type[MODEL], key: Any - ) -> Union[QuerySetSingle[MODEL], _GenericAlias]: - if (isinstance(key, tuple) and inspect.isclass(key[0])) or inspect.isclass(key): - return _GenericAlias(cls, key) + def __class_getitem__(cls: Type[MODEL], key: Any) -> QuerySetSingle[MODEL]: # type: ignore + if ((isinstance(key, tuple) and inspect.isclass(key[0])) or inspect.isclass(key)) and issubclass(cls, Generic): + return Generic.__class_getitem__.__func__(cls, key) return cls._getbypk(key) + def _set_kwargs(self, kwargs: dict) -> Set[str]: meta = self._meta From f040425cbacd32dc4ea3a40ab03fdcdae773017b Mon Sep 17 00:00:00 2001 From: Robin Schyboll Date: Thu, 25 Nov 2021 14:52:01 +0000 Subject: [PATCH 3/5] Fixed code style issues --- tortoise/models.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tortoise/models.py b/tortoise/models.py index 5d80f480e..002764d7d 100644 --- a/tortoise/models.py +++ b/tortoise/models.py @@ -9,6 +9,7 @@ Callable, Dict, Generator, + Generic, Iterable, List, Optional, @@ -17,7 +18,6 @@ Type, TypeVar, Union, - Generic, ) from pypika import Order, Query, Table @@ -662,11 +662,12 @@ def __init__(self, **kwargs: Any) -> None: setattr(self, key, field_object.default) def __class_getitem__(cls: Type[MODEL], key: Any) -> QuerySetSingle[MODEL]: # type: ignore - if ((isinstance(key, tuple) and inspect.isclass(key[0])) or inspect.isclass(key)) and issubclass(cls, Generic): + if ( + (isinstance(key, tuple) and inspect.isclass(key[0])) or inspect.isclass(key) + ) and issubclass(cls, Generic): return Generic.__class_getitem__.__func__(cls, key) return cls._getbypk(key) - def _set_kwargs(self, kwargs: dict) -> Set[str]: meta = self._meta From ab60e66c8393dd8fc3aed0b5cdf030ac1025d97a Mon Sep 17 00:00:00 2001 From: Robin Schyboll Date: Fri, 26 Nov 2021 19:48:41 +0000 Subject: [PATCH 4/5] Fixed mypy typing issues Simply added #type: ignore comments, due to type checkers not working with __class_getitem__ properly yet. The old __getitem__ method was also disabled from type checking. --- tortoise/models.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tortoise/models.py b/tortoise/models.py index 002764d7d..4fd94218c 100644 --- a/tortoise/models.py +++ b/tortoise/models.py @@ -661,12 +661,11 @@ def __init__(self, **kwargs: Any) -> None: else: setattr(self, key, field_object.default) - def __class_getitem__(cls: Type[MODEL], key: Any) -> QuerySetSingle[MODEL]: # type: ignore - if ( - (isinstance(key, tuple) and inspect.isclass(key[0])) or inspect.isclass(key) - ) and issubclass(cls, Generic): - return Generic.__class_getitem__.__func__(cls, key) - return cls._getbypk(key) + def __class_getitem__(cls: Type[MODEL], key: Any) -> QuerySetSingle[MODEL]: + if (isinstance(key, tuple) and inspect.isclass(key[0])) or inspect.isclass(key): + if issubclass(cls, Generic): # type: ignore + return Generic.__class_getitem__.__func__(cls, key) # type: ignore + return cls._getbypk(key) # type: ignore def _set_kwargs(self, kwargs: dict) -> Set[str]: meta = self._meta From 1b1b653bcd313a924fa94711d513dfe7df0ce754 Mon Sep 17 00:00:00 2001 From: Robin Schyboll Date: Thu, 20 Jan 2022 21:26:48 +0000 Subject: [PATCH 5/5] Added docstring to __class_getitem__ method --- tortoise/models.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tortoise/models.py b/tortoise/models.py index 4fd94218c..7a3799909 100644 --- a/tortoise/models.py +++ b/tortoise/models.py @@ -662,6 +662,10 @@ def __init__(self, **kwargs: Any) -> None: setattr(self, key, field_object.default) def __class_getitem__(cls: Type[MODEL], key: Any) -> QuerySetSingle[MODEL]: + """ + Creates a QuerySetSingle when accessed key is not a Class, + or a Generic class, when key is a class. + """ if (isinstance(key, tuple) and inspect.isclass(key[0])) or inspect.isclass(key): if issubclass(cls, Generic): # type: ignore return Generic.__class_getitem__.__func__(cls, key) # type: ignore