tortoise / tortoise-orm

Familiar asyncio ORM for python, built with relations in mind
https://tortoise.github.io
Apache License 2.0
4.64k stars 386 forks source link

NumericField make #1691

Open daeeros opened 2 months ago

daeeros commented 2 months ago

I would like to create a numericfield that is floatless and has a large limit, allowing me to store giant numbers. But how to make one on tortoise can you tell me? Here is my old implementation on peewee

class NumericField(pw.DecimalField):
    def get_modifiers(self):
        pass

    def python_value(self, value):
        # Convert the decimal value to int before returning
        if value is not None:
            return int(value)
        return value

test = NumericField(default=10000)
waketzheng commented 2 months ago

How about BigIntField

daeeros commented 2 months ago

How about BigIntField

They have max 9,223,372,036,854,775,807. Sometime i need bigger then this value to store(

waketzheng commented 2 months ago

@daeeros A sample for you:

from typing import Any, Type

from tortoise import run_async
from tortoise.contrib.test import init_memory_sqlite
from tortoise.fields import Field, IntField
from tortoise.fields.data import SqlTypes, Term, functions
from tortoise.models import Model

class BigBigIntField(Field[int], int):
    SQL_TYPE = "DECIMAL(40,0)"

    def to_python_value(self, value: Any) -> int | None:
        self.validate(value)
        if value is not None:
            value = int(value)
        return value

    def to_db_value(self, value: Any, instance: Type[Model] | Model) -> str | None:
        if value is not None:
            value = str(value)
        self.validate(value)
        return value

    class _db_sqlite:
        SQL_TYPE = "VARCHAR(40)"

        def function_cast(self, term: Term) -> Term:
            return functions.Cast(term, SqlTypes.NUMERIC)

class User(Model):
    id = IntField(pk=True)
    money = BigBigIntField(default=10000)

@init_memory_sqlite
async def main() -> None:
    u = await User.create()
    print(u.money)
    a = 9_223_372_036_854_775_807
    u = await User.create(money=a * 10)
    print(u.money)
    print(f"{type(u.money)= }")
    assert u.money == a * 10

if __name__ == "__main__":
    run_async(main())

Pass test with sqlite and mysql.

daeeros commented 2 months ago

@daeeros A sample for you:

from typing import Any, Type

from tortoise import run_async
from tortoise.contrib.test import init_memory_sqlite
from tortoise.fields import Field, IntField
from tortoise.fields.data import SqlTypes, Term, functions
from tortoise.models import Model

class BigBigIntField(Field[int], int):
    SQL_TYPE = "DECIMAL(40,0)"

    def to_python_value(self, value: Any) -> int | None:
        self.validate(value)
        if value is not None:
            value = int(value)
        return value

    def to_db_value(self, value: Any, instance: Type[Model] | Model) -> str | None:
        if value is not None:
            value = str(value)
        self.validate(value)
        return value

    class _db_sqlite:
        SQL_TYPE = "VARCHAR(40)"

        def function_cast(self, term: Term) -> Term:
            return functions.Cast(term, SqlTypes.NUMERIC)

class User(Model):
    id = IntField(pk=True)
    money = BigBigIntField(default=10000)

@init_memory_sqlite
async def main() -> None:
    u = await User.create()
    print(u.money)
    a = 9_223_372_036_854_775_807
    u = await User.create(money=a * 10)
    print(u.money)
    print(f"{type(u.money)= }")
    assert u.money == a * 10

if __name__ == "__main__":
    run_async(main())

Pass test with sqlite and mysql.

I'm using Postgresql, is it for it?

waketzheng commented 2 months ago

Sure.