-
-
Notifications
You must be signed in to change notification settings - Fork 391
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add uuid compression storing data as
binary
(#1458)
* feat: add uuid compression storing data as `binary` * style: fix trailing whitespaces * docs: add changes to `CHANGELOG.rst` * ref: move changes to `tortoise/contrib/mysql/fields.py` * fix: error about unconsistent methods * fix: inherits from base field instead old `UUIDField` This's becaus the old `UUIDField` class is not a generic class. * style: format code * test: add test for MySQL `UUIDField` class * fix: remove bugs and lint errors * chore: add missing parameter in method overriding * chore: fix linter errors * style: fix style * style: fix types to pass code quality * style: fix types to pass code quality * chore: bypass linter false-positive * docs: update documentation
- Loading branch information
Showing
7 changed files
with
190 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import uuid | ||
|
||
from tests import testmodels_mysql | ||
from tortoise.contrib import test | ||
from tortoise.exceptions import IntegrityError | ||
|
||
|
||
class TestMySQLUUIDFields(test.TestCase): | ||
async def test_empty(self): | ||
with self.assertRaises(IntegrityError): | ||
await testmodels_mysql.UUIDFields.create() | ||
|
||
async def test_create(self): | ||
data = uuid.uuid4() | ||
obj0 = await testmodels_mysql.UUIDFields.create(data=data) | ||
self.assertIsInstance(obj0.data, bytes) | ||
self.assertIsInstance(obj0.data_auto, bytes) | ||
self.assertEqual(obj0.data_null, None) | ||
obj = await testmodels_mysql.UUIDFields.get(id=obj0.id) | ||
self.assertIsInstance(obj.data, uuid.UUID) | ||
self.assertIsInstance(obj.data_auto, uuid.UUID) | ||
self.assertEqual(obj.data, data) | ||
self.assertEqual(obj.data_null, None) | ||
await obj.save() | ||
obj2 = await testmodels_mysql.UUIDFields.get(id=obj.id) | ||
self.assertEqual(obj, obj2) | ||
|
||
await obj.delete() | ||
obj = await testmodels_mysql.UUIDFields.filter(id=obj0.id).first() | ||
self.assertEqual(obj, None) | ||
|
||
async def test_update(self): | ||
data = uuid.uuid4() | ||
data2 = uuid.uuid4() | ||
obj0 = await testmodels_mysql.UUIDFields.create(data=data) | ||
await testmodels_mysql.UUIDFields.filter(id=obj0.id).update(data=data2) | ||
obj = await testmodels_mysql.UUIDFields.get(id=obj0.id) | ||
self.assertEqual(obj.data, data2) | ||
self.assertEqual(obj.data_null, None) | ||
|
||
async def test_create_not_null(self): | ||
data = uuid.uuid4() | ||
obj0 = await testmodels_mysql.UUIDFields.create(data=data, data_null=data) | ||
obj = await testmodels_mysql.UUIDFields.get(id=obj0.id) | ||
self.assertEqual(obj.data, data) | ||
self.assertEqual(obj.data_null, data) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
from tortoise import fields | ||
from tortoise.contrib.mysql import fields as mysql_fields | ||
from tortoise.models import Model | ||
|
||
|
||
class UUIDPkModel(Model): | ||
id = mysql_fields.UUIDField(pk=True) | ||
|
||
children: fields.ReverseRelation["UUIDFkRelatedModel"] | ||
children_null: fields.ReverseRelation["UUIDFkRelatedNullModel"] | ||
peers: fields.ManyToManyRelation["UUIDM2MRelatedModel"] | ||
|
||
|
||
class UUIDFkRelatedModel(Model): | ||
id = mysql_fields.UUIDField(pk=True) | ||
name = fields.CharField(max_length=50, null=True) | ||
model: fields.ForeignKeyRelation[UUIDPkModel] = fields.ForeignKeyField( | ||
"models.UUIDPkModel", related_name="children" | ||
) | ||
|
||
|
||
class UUIDFkRelatedNullModel(Model): | ||
id = mysql_fields.UUIDField(pk=True) | ||
name = fields.CharField(max_length=50, null=True) | ||
model: fields.ForeignKeyNullableRelation[UUIDPkModel] = fields.ForeignKeyField( | ||
"models.UUIDPkModel", related_name=False, null=True | ||
) | ||
parent: fields.OneToOneNullableRelation[UUIDPkModel] = fields.OneToOneField( | ||
"models.UUIDPkModel", related_name=False, null=True, on_delete=fields.NO_ACTION | ||
) | ||
|
||
|
||
class UUIDM2MRelatedModel(Model): | ||
id = mysql_fields.UUIDField(pk=True) | ||
value = fields.TextField(default="test") | ||
models: fields.ManyToManyRelation[UUIDPkModel] = fields.ManyToManyField( | ||
"models.UUIDPkModel", related_name="peers" | ||
) | ||
|
||
|
||
class UUIDPkSourceModel(Model): | ||
id = mysql_fields.UUIDField(pk=True, source_field="a") | ||
|
||
class Meta: | ||
table = "upsm" | ||
|
||
|
||
class UUIDFkRelatedSourceModel(Model): | ||
id = mysql_fields.UUIDField(pk=True, source_field="b") | ||
name = fields.CharField(max_length=50, null=True, source_field="c") | ||
model: fields.ForeignKeyRelation[UUIDPkSourceModel] = fields.ForeignKeyField( | ||
"models.UUIDPkSourceModel", related_name="children", source_field="d" | ||
) | ||
|
||
class Meta: | ||
table = "ufrsm" | ||
|
||
|
||
class UUIDFkRelatedNullSourceModel(Model): | ||
id = mysql_fields.UUIDField(pk=True, source_field="i") | ||
name = fields.CharField(max_length=50, null=True, source_field="j") | ||
model: fields.ForeignKeyNullableRelation[UUIDPkSourceModel] = fields.ForeignKeyField( | ||
"models.UUIDPkSourceModel", related_name="children_null", source_field="k", null=True | ||
) | ||
|
||
class Meta: | ||
table = "ufrnsm" | ||
|
||
|
||
class UUIDM2MRelatedSourceModel(Model): | ||
id = mysql_fields.UUIDField(pk=True, source_field="e") | ||
value = fields.TextField(default="test", source_field="f") | ||
models: fields.ManyToManyRelation[UUIDPkSourceModel] = fields.ManyToManyField( | ||
"models.UUIDPkSourceModel", related_name="peers", forward_key="e", backward_key="h" | ||
) | ||
|
||
class Meta: | ||
table = "umrsm" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,63 @@ | ||
from typing import ( # noqa pylint: disable=unused-import | ||
TYPE_CHECKING, | ||
Any, | ||
Optional, | ||
Type, | ||
Union, | ||
) | ||
from uuid import UUID, uuid4 | ||
|
||
from tortoise.fields import Field | ||
from tortoise.fields import UUIDField as UUIDFieldBase | ||
|
||
if TYPE_CHECKING: # pragma: nocoverage | ||
from tortoise.models import Model # noqa pylint: disable=unused-import | ||
|
||
|
||
class GeometryField(Field): | ||
SQL_TYPE = "GEOMETRY" | ||
|
||
|
||
class UUIDField(UUIDFieldBase): | ||
""" | ||
UUID Field | ||
This field can store uuid value, but with the option to add binary compression. | ||
If used as a primary key, it will auto-generate a UUID4 by default. | ||
``binary_compression`` (bool): | ||
If True, the UUID will be stored in binary format. | ||
This will save 6 bytes per UUID in the database. | ||
Note: that this is a MySQL-only feature. | ||
See https://dev.mysql.com/blog-archive/mysql-8-0-uuid-support/ for more details. | ||
""" | ||
|
||
SQL_TYPE = "CHAR(36)" | ||
|
||
def __init__(self, binary_compression: bool = True, **kwargs: Any) -> None: | ||
if kwargs.get("pk", False) and "default" not in kwargs: | ||
kwargs["default"] = uuid4 | ||
super().__init__(**kwargs) | ||
|
||
if binary_compression: | ||
self.SQL_TYPE = "BINARY(16)" | ||
self._binary_compression = binary_compression | ||
|
||
def to_db_value(self, value: Any, instance: "Union[Type[Model], Model]") -> Optional[Union[str, bytes]]: # type: ignore | ||
# Make sure that value is a UUIDv4 | ||
# If not, raise an error | ||
# This is to prevent UUIDv1 or any other version from being stored in the database | ||
if self._binary_compression: | ||
if value is not isinstance(value, UUID): | ||
raise ValueError("UUIDField only accepts UUID values") | ||
return value.bytes | ||
return value and str(value) | ||
|
||
def to_python_value(self, value: Any) -> Optional[UUID]: | ||
if value is None or isinstance(value, UUID): | ||
return value | ||
elif self._binary_compression and isinstance(value, bytes): | ||
return UUID(bytes=value) | ||
else: | ||
return UUID(value) |