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

feat: add mixin for int enum field custom #1781

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from

Conversation

waketzheng
Copy link
Contributor

@waketzheng waketzheng commented Nov 21, 2024

Description

As #966 said, IntEnumField inherit from SmallIntField, so it can not use a enum_type with value > 32767

Motivation and Context

This PR add a IntEnumFieldMixin to make it easy to custom a new IntEnumField:

from typing import Type, cast
from tortoise.fields.data import IntEnumFieldMixin, IntEnumType, IntField

class IntEnumInstance(IntEnumFieldMixin, IntField):
    pass

def IntEnumField(enum_type: Type[IntEnumType], **kwargs) -> IntEnumType:
    return cast(IntEnumType, IntEnumInstance(enum_type, **kwargs))

How Has This Been Tested?

make ci

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have added the changelog accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

@waketzheng waketzheng changed the title feat: auto choose field type for int enum field feat: auto choose field instance class for int enum field Nov 21, 2024
@waketzheng waketzheng force-pushed the feat-auto-choose-field-type-for-IntEnumField branch from 725ff99 to 46f2bb1 Compare November 21, 2024 06:46
@coveralls
Copy link

coveralls commented Nov 21, 2024

Pull Request Test Coverage Report for Build 12028090078

Details

  • 14 of 14 (100.0%) changed or added relevant lines in 1 file are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.006%) to 89.538%

Totals Coverage Status
Change from base Build 12007385729: 0.006%
Covered Lines: 6250
Relevant Lines: 6872

💛 - Coveralls

Copy link
Contributor

@henadzit henadzit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about making IntEnumFieldInstance inheriting the regular IntField which will extend the potential values up to 2147483647? This should be enough for 99.9999% cases. If someone needs more, they can create a custom field.

I think we want to keep our Fields simple.

tortoise/fields/data.py Outdated Show resolved Hide resolved
@waketzheng waketzheng force-pushed the feat-auto-choose-field-type-for-IntEnumField branch from 46f2bb1 to f0884f4 Compare November 22, 2024 18:03
@waketzheng waketzheng changed the title feat: auto choose field instance class for int enum field feat: add mixin for int enum field custom Nov 24, 2024
Copy link
Contributor

@henadzit henadzit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think we need to make IntEnumField an integer field (which is going to require a migration for users unfortunately but this would be a correct way to do it), not a small integer field. This will cover 99.9% cases and we won't have to complicate the code and making extra documentation for a very rare edge case. For the people who need big integers, they can just copy the related code from data.py and make their own field.

tortoise/fields/data.py Outdated Show resolved Hide resolved
class IntEnumFieldInstance(IntEnumFieldMixin, SmallIntField):
pass


def IntEnumField(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really confusing that IntEnumField actually returns SmallIntField :(

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but mostly, IntEnumField used in the following case:

class Gender(IntEnum):
      male = 0
      female = 1
      other = 2

class Users(Model):
    gender = fields.IntEnumField(Gender)

And I wish it to be small int in db, not int.

@waketzheng
Copy link
Contributor Author

I still think we need to make IntEnumField an integer field (which is going to require a migration for users unfortunately but this would be a correct way to do it), not a small integer field. This will cover 99.9% cases and we won't have to complicate the code and making extra documentation for a very rare edge case. For the people who need big integers, they can just copy the related code from data.py and make their own field.

I tried this one:

-class IntEnumFieldInstance(SmallIntField):
+class IntEnumFieldInstance(IntField):

And then run aerich migrate, it generated a new migration file:

from tortoise import BaseDBAsyncClient


async def upgrade(db: BaseDBAsyncClient) -> str:
    return """
        ALTER TABLE "users" ALTER COLUMN "gender" TYPE INT USING "gender"::INT;"""


async def downgrade(db: BaseDBAsyncClient) -> str:
    return """
        ALTER TABLE "users" ALTER COLUMN "gender" TYPE SMALLINT USING "gender"::SMALLINT;"""

  1. db field type migrating is boring, especially for projects running in production
  2. I strongly suggest to keep backward compatibility until the next major version

@abondar
Copy link
Member

abondar commented Nov 28, 2024

I am more inclined to have default enum as smallint, as it is indeed covers most sane cases of using enums

Although maybe we should document it somehow better

@henadzit
Copy link
Contributor

Let's see how other ORMs implement this!

Django

In Django you choose the field, e.g. CharField, SmallIntegerField, IntegerField, etc. and then you specify choices. Basically it gives the user flexibility to choose the field type.

SQLAlchemy

SQLAlchemy "will make use of the backend’s native “ENUM” type if one is available; otherwise, it uses a VARCHAR datatype".

Native ENUMs are great but Django's approach is more universal and supported by more databases and I like it more personally. I think we can consider implementing something like that in Tortoise instead of the existing enum approach but not as part of this PR obviously.

I agree with Andrey that small int should cover most sane use cases of using enums. I don't like the idea of introducing mixins and making the fields classes and documentation more complicated for resolving some very peculiar use case. I think we can just update the docstring of IntEnumField to say that it is a small int. We can also post in the issue that provoked this PR that they can copy the IntEnumFieldInstance code reimplement it to support other data types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants