Fix create/drop indexes in every migration (#377)

* Add `__eq__` method for `Index`instances

* tests: add Index test case

* refactor: compare index instances before set hash and eq func to class

* fix: sort fields when generating index hash

* docs: update changlog

* fix style issue

* refactor: use CustomIndex instead of postgres special HashIndex

* Check tortoise version before patch Index

* Add comment

* Add comment for why > work

---------

Co-authored-by: dbf <somnium@riseup.net>
This commit is contained in:
Waket Zheng 2024-12-22 00:24:18 +08:00 committed by GitHub
parent f93faa8afb
commit 7d22518c74
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 44 additions and 15 deletions

View File

@ -16,6 +16,7 @@
- Fix configuration file reading error when containing Chinese characters. (#286) - Fix configuration file reading error when containing Chinese characters. (#286)
- sqlite: failed to create/drop index. (#302) - sqlite: failed to create/drop index. (#302)
- PostgreSQL: Cannot drop constraint after deleting or rename FK on a model. (#378) - PostgreSQL: Cannot drop constraint after deleting or rename FK on a model. (#378)
- Fix create/drop indexes in every migration. (#377)
- Sort m2m fields before comparing them with diff. (#271) - Sort m2m fields before comparing them with diff. (#271)
#### Changed #### Changed

View File

@ -1,4 +1,5 @@
import hashlib from __future__ import annotations
import importlib import importlib
import os import os
from datetime import datetime from datetime import datetime
@ -6,6 +7,7 @@ from pathlib import Path
from typing import Dict, Iterable, List, Optional, Set, Tuple, Type, Union, cast from typing import Dict, Iterable, List, Optional, Set, Tuple, Type, Union, cast
import asyncclick as click import asyncclick as click
import tortoise
from dictdiffer import diff from dictdiffer import diff
from tortoise import BaseDBAsyncClient, Model, Tortoise from tortoise import BaseDBAsyncClient, Model, Tortoise
from tortoise.exceptions import OperationalError from tortoise.exceptions import OperationalError
@ -202,21 +204,25 @@ class Migrate:
@classmethod @classmethod
def _handle_indexes(cls, model: Type[Model], indexes: List[Union[Tuple[str], Index]]) -> list: def _handle_indexes(cls, model: Type[Model], indexes: List[Union[Tuple[str], Index]]) -> list:
ret: list = [] if tortoise.__version__ > "0.22.2":
# The min version of tortoise is '0.11.0', so we can compare it by a `>`,
# tortoise>0.22.2 have __eq__/__hash__ with Index class since 313ee76.
return indexes
if index_classes := set(index.__class__ for index in indexes if isinstance(index, Index)):
# Leave magic patch here to compare with older version of tortoise-orm
# TODO: limit tortoise>0.22.2 in pyproject.toml and remove this function when v0.9.0 released
for index_cls in index_classes:
if index_cls(fields=("id",)) != index_cls(fields=("id",)):
def index_hash(self) -> str: def _hash(self) -> int:
h = hashlib.new("MD5", usedforsecurity=False) # type:ignore[call-arg] return hash((tuple(sorted(self.fields)), self.name, self.expressions))
h.update(
self.index_name(cls.ddl.schema_generator, model).encode()
+ self.__class__.__name__.encode()
)
return h.hexdigest()
for index in indexes: def _eq(self, other) -> bool:
if isinstance(index, Index): return type(self) is type(other) and self.__dict__ == other.__dict__
index.__hash__ = index_hash # type:ignore[method-assign,assignment]
ret.append(index) setattr(index_cls, "__hash__", _hash)
return ret setattr(index_cls, "__eq__", _eq)
return indexes
@classmethod @classmethod
def _get_indexes(cls, model, model_describe: dict) -> Set[Union[Index, Tuple[str, ...]]]: def _get_indexes(cls, model, model_describe: dict) -> Set[Union[Index, Tuple[str, ...]]]:

7
tests/indexes.py Normal file
View File

@ -0,0 +1,7 @@
from tortoise.indexes import Index
class CustomIndex(Index):
def __init__(self, *args, **kw) -> None:
super().__init__(*args, **kw)
self._foo = ""

View File

@ -3,6 +3,9 @@ import uuid
from enum import IntEnum from enum import IntEnum
from tortoise import Model, fields from tortoise import Model, fields
from tortoise.indexes import Index
from tests.indexes import CustomIndex
class ProductType(IntEnum): class ProductType(IntEnum):
@ -33,6 +36,10 @@ class User(Model):
products: fields.ManyToManyRelation["Product"] products: fields.ManyToManyRelation["Product"]
class Meta:
# reverse indexes elements
indexes = [CustomIndex(fields=("is_superuser",)), Index(fields=("username", "is_active"))]
class Email(Model): class Email(Model):
email_id = fields.IntField(primary_key=True) email_id = fields.IntField(primary_key=True)

View File

@ -2,6 +2,9 @@ import datetime
from enum import IntEnum from enum import IntEnum
from tortoise import Model, fields from tortoise import Model, fields
from tortoise.indexes import Index
from tests.indexes import CustomIndex
class ProductType(IntEnum): class ProductType(IntEnum):
@ -31,6 +34,9 @@ class User(Model):
intro = fields.TextField(default="") intro = fields.TextField(default="")
longitude = fields.DecimalField(max_digits=12, decimal_places=9) longitude = fields.DecimalField(max_digits=12, decimal_places=9)
class Meta:
indexes = [Index(fields=("username", "is_active")), CustomIndex(fields=("is_superuser",))]
class Email(Model): class Email(Model):
email = fields.CharField(max_length=200) email = fields.CharField(max_length=200)

View File

@ -3,6 +3,7 @@ from pathlib import Path
import pytest import pytest
import tortoise import tortoise
from pytest_mock import MockerFixture from pytest_mock import MockerFixture
from tortoise.indexes import Index
from aerich.ddl.mysql import MysqlDDL from aerich.ddl.mysql import MysqlDDL
from aerich.ddl.postgres import PostgresDDL from aerich.ddl.postgres import PostgresDDL
@ -10,6 +11,7 @@ from aerich.ddl.sqlite import SqliteDDL
from aerich.exceptions import NotSupportError from aerich.exceptions import NotSupportError
from aerich.migrate import MIGRATE_TEMPLATE, Migrate from aerich.migrate import MIGRATE_TEMPLATE, Migrate
from aerich.utils import get_models_describe from aerich.utils import get_models_describe
from tests.indexes import CustomIndex
# tortoise-orm>=0.21 changes IntField constraints # tortoise-orm>=0.21 changes IntField constraints
# from {"ge": 1, "le": 2147483647} to {"ge": -2147483648, "le": 2147483647} # from {"ge": 1, "le": 2147483647} to {"ge": -2147483648, "le": 2147483647}
@ -608,7 +610,7 @@ old_models_describe = {
"description": None, "description": None,
"docstring": None, "docstring": None,
"unique_together": [], "unique_together": [],
"indexes": [], "indexes": [Index(fields=("username", "is_active")), CustomIndex(fields=("is_superuser",))],
"pk_field": { "pk_field": {
"name": "id", "name": "id",
"field_type": "IntField", "field_type": "IntField",