Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-added support for __set_name__ as the Python class datamodel presc… #1084

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Changelog
Added
^^^^^
- Added psycopg backend support.
- Added __set_name__ support for custom fields
Fixed
^^^^^
- Fix `bulk_create` doesn't work correctly with more than 1 update_fields. (#1046)
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ Contributors
* Vinay Karanam ``@vinayinvicible``
* Aleksandr Rozum ``@rozumalex``
* Mojix Coder ``@MojixCoder``
* Rick van Hattem ``@wolph``

Special Thanks
==============
Expand Down
10 changes: 10 additions & 0 deletions tests/fields/subclass_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,13 @@ def to_python_value(self, value: Any) -> Any:
return self.enum_type(value)
except ValueError:
raise ValueError(f"Database value {value} does not exist on Enum {self.enum_type}.")


class NamedField(CharField):
def __init__(self, name: str, **kwargs):
super().__init__(128, **kwargs)
self.name = name

def __set_name__(self, owner: Type[Any], name: str) -> None:
if self.name != name:
raise RuntimeError(f"Field name {name} does not match {self.name}")
6 changes: 5 additions & 1 deletion tests/fields/subclass_models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from enum import Enum, IntEnum

from tests.fields.subclass_fields import EnumField, IntEnumField
from tests.fields.subclass_fields import EnumField, IntEnumField, NamedField
from tortoise import fields
from tortoise.models import Model

Expand Down Expand Up @@ -29,3 +29,7 @@ class ContactTypeEnum(IntEnum):
class Contact(Model):
id = fields.IntField(pk=True)
type = IntEnumField(ContactTypeEnum, default=ContactTypeEnum.other)


class NamedFields(Model):
a = NamedField("a")
9 changes: 9 additions & 0 deletions tortoise/fields/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,15 @@ def __init__(
self.model: Type["Model"] = model # type: ignore
self.reference: "Optional[Field]" = None

def __set_name__(self, owner: Type[Any], name: str) -> None:
"""
Set the name of the field on the model and the model.

Needed because Mypy is not yet __set_name__ aware:
https://github.com/python/mypy/issues/8057
"""
...

def to_db_value(self, value: Any, instance: "Union[Type[Model], Model]") -> Any:
"""
Converts from the Python type to the DB type.
Expand Down
7 changes: 6 additions & 1 deletion tortoise/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -619,9 +619,14 @@ def __search_for_field_attributes(base: Type, attrs: dict) -> None:
meta.abstract = True

new_class = super().__new__(mcs, name, bases, attrs)
for field in meta.fields_map.values():
for name, field in meta.fields_map.items():
field.model = new_class # type: ignore

# Call __set_name__ so fields know their own attribute name and parent model
# https://docs.python.org/3/reference/datamodel.html#object.__set_name__
if hasattr(field, "__set_name__"):
field.__set_name__(new_class, name)

for fname, comment in _get_comments(new_class).items(): # type: ignore
if fname in fields_map:
fields_map[fname].docstring = comment
Expand Down