From 7ac5b7dc32ed0802ef413fe7d53caaf05732b1dc Mon Sep 17 00:00:00 2001 From: romanov Date: Wed, 10 Aug 2022 16:26:51 +0300 Subject: [PATCH 1/6] CREATE and DROP uniq constraint --- cli.py | 143 ++++++++++++++++++ main.py | 13 ++ piccolo/__init__.py | 2 +- .../apps/migrations/auto/diffable_table.py | 3 +- .../apps/migrations/auto/migration_manager.py | 67 +++++--- piccolo/apps/migrations/auto/operations.py | 1 + piccolo/apps/migrations/auto/schema_differ.py | 20 ++- piccolo/columns/constraints.py | 50 ++++++ piccolo/query/methods/alter.py | 21 +++ piccolo/table.py | 7 + piccolo_conf.py | 6 +- test_app/__init__.py | 0 test_app/piccolo_app.py | 22 +++ .../2022-08-10T15-10-37-698407.py | 78 ++++++++++ .../2022-08-10T15-11-07-637432.py | 25 +++ .../2022-08-10T16-19-25-729836.py | 23 +++ test_app/piccolo_migrations/__init__.py | 0 test_app/tables.py | 11 ++ 18 files changed, 467 insertions(+), 25 deletions(-) create mode 100644 cli.py create mode 100644 main.py create mode 100644 piccolo/columns/constraints.py create mode 100644 test_app/__init__.py create mode 100644 test_app/piccolo_app.py create mode 100644 test_app/piccolo_migrations/2022-08-10T15-10-37-698407.py create mode 100644 test_app/piccolo_migrations/2022-08-10T15-11-07-637432.py create mode 100644 test_app/piccolo_migrations/2022-08-10T16-19-25-729836.py create mode 100644 test_app/piccolo_migrations/__init__.py create mode 100644 test_app/tables.py diff --git a/cli.py b/cli.py new file mode 100644 index 000000000..a175ddea4 --- /dev/null +++ b/cli.py @@ -0,0 +1,143 @@ +import os +import sys + +from targ import CLI + +try: + import uvloop # type: ignore + + uvloop.install() +except ImportError: + pass + +from piccolo.apps.app.piccolo_app import APP_CONFIG as app_config +from piccolo.apps.asgi.piccolo_app import APP_CONFIG as asgi_config +from piccolo.apps.fixtures.piccolo_app import APP_CONFIG as fixtures_config +from piccolo.apps.meta.piccolo_app import APP_CONFIG as meta_config +from piccolo.apps.migrations.commands.check import CheckMigrationManager +from piccolo.apps.migrations.piccolo_app import APP_CONFIG as migrations_config +from piccolo.apps.playground.piccolo_app import APP_CONFIG as playground_config +from piccolo.apps.project.piccolo_app import APP_CONFIG as project_config +from piccolo.apps.schema.piccolo_app import APP_CONFIG as schema_config +from piccolo.apps.shell.piccolo_app import APP_CONFIG as shell_config +from piccolo.apps.sql_shell.piccolo_app import APP_CONFIG as sql_shell_config +from piccolo.apps.tester.piccolo_app import APP_CONFIG as tester_config +from piccolo.apps.user.piccolo_app import APP_CONFIG as user_config +from piccolo.conf.apps import AppRegistry, Finder +from piccolo.utils.sync import run_sync +from piccolo.utils.warnings import Level, colored_warning + +DIAGNOSE_FLAG = "--diagnose" + + +def get_diagnose_flag() -> bool: + return DIAGNOSE_FLAG in sys.argv + + +def main(): + """ + The entrypoint to the Piccolo CLI. + """ + # In case it's run from an entrypoint: + sys.path.insert(0, os.getcwd()) + + ########################################################################### + # Run in diagnose mode if requested. + + diagnose = get_diagnose_flag() + if diagnose: + print("Diagnosis...") + if Finder(diagnose=True).get_app_registry(): + print("Everything OK") + return + + ########################################################################### + + cli = CLI(description="Piccolo CLI") + + ########################################################################### + # Register the base apps. + + for _app_config in [ + app_config, + asgi_config, + fixtures_config, + meta_config, + migrations_config, + playground_config, + project_config, + schema_config, + shell_config, + sql_shell_config, + tester_config, + user_config, + ]: + for command in _app_config.commands: + cli.register( + command.callable, + group_name=_app_config.app_name, + aliases=command.aliases, + ) + + ########################################################################### + # Get user defined apps. + + try: + APP_REGISTRY: AppRegistry = Finder().get_app_registry() + except (ImportError, AttributeError): + print( + "Can't import the APP_REGISTRY from piccolo_conf - some " + "commands may be missing. If this is a new project don't worry. " + f"To see a full traceback use `piccolo {DIAGNOSE_FLAG}`" + ) + else: + for app_name, _app_config in APP_REGISTRY.app_configs.items(): + for command in _app_config.commands: + if cli.command_exists( + group_name=app_name, command_name=command.callable.__name__ + ): + # Skipping - already registered. + continue + cli.register( + command.callable, + group_name=app_name, + aliases=command.aliases, + ) + + if "migrations" not in sys.argv: + # Show a warning if any migrations haven't been run. + # Don't run it if it looks like the user is running a migration + # command, as this information is redundant. + + try: + havent_ran_count = run_sync( + CheckMigrationManager(app_name="all").havent_ran_count() + ) + if havent_ran_count: + message = ( + f"{havent_ran_count} migration hasn't" + if havent_ran_count == 1 + else f"{havent_ran_count} migrations haven't" + ) + + colored_warning( + message=( + "=> {} been run - the app " + "might not behave as expected.\n" + "To check which use:\n" + " piccolo migrations check\n" + "To run all migrations:\n" + " piccolo migrations forwards all\n" + ).format(message), + level=Level.high, + ) + except Exception: + pass + + ########################################################################### + + cli.run() + + +if __name__ == "__main__": + main() diff --git a/main.py b/main.py new file mode 100644 index 000000000..4d5254987 --- /dev/null +++ b/main.py @@ -0,0 +1,13 @@ +from piccolo.engine.postgres import PostgresEngine +from piccolo.table import Table +from piccolo.columns import ForeignKey, Integer, Varchar + + +# connection_string: str = 'postgresql://test:test@127.0.0.1:5555/test' +# engine = PostgresEngine(config={'dsn':connection_string}) + + +# class Band(Table): +# name = Varchar(length=100) +# popularity = Integer() + diff --git a/piccolo/__init__.py b/piccolo/__init__.py index 5cdb6dfa7..211046fd7 100644 --- a/piccolo/__init__.py +++ b/piccolo/__init__.py @@ -1 +1 @@ -__VERSION__ = "0.82.0" +__VERSION__ = "0.82.0" \ No newline at end of file diff --git a/piccolo/apps/migrations/auto/diffable_table.py b/piccolo/apps/migrations/auto/diffable_table.py index 18a7c2809..cc929b79c 100644 --- a/piccolo/apps/migrations/auto/diffable_table.py +++ b/piccolo/apps/migrations/auto/diffable_table.py @@ -132,13 +132,13 @@ def __sub__(self, value: DiffableTable) -> TableDelta: key=lambda x: x.column._meta.name, ) ] - drop_columns = [ DropColumn( table_class_name=self.class_name, column_name=i.column._meta.name, db_column_name=i.column._meta.db_column_name, tablename=value.tablename, + column_class=i.column.__class__ ) for i in sorted( {ColumnComparison(column=column) for column in value.columns} @@ -146,7 +146,6 @@ def __sub__(self, value: DiffableTable) -> TableDelta: key=lambda x: x.column._meta.name, ) ] - ####################################################################### alter_columns: t.List[AlterColumn] = [] diff --git a/piccolo/apps/migrations/auto/migration_manager.py b/piccolo/apps/migrations/auto/migration_manager.py index a03407efb..2ddf5d17e 100644 --- a/piccolo/apps/migrations/auto/migration_manager.py +++ b/piccolo/apps/migrations/auto/migration_manager.py @@ -14,10 +14,11 @@ from piccolo.apps.migrations.auto.serialisation import deserialise_params from piccolo.columns import Column, column_types from piccolo.columns.column_types import Serial +from piccolo.columns.constraints import UniqueConstraint from piccolo.engine import engine_finder from piccolo.table import Table, create_table_class, sort_table_classes from piccolo.utils.warnings import colored_warning - +from piccolo.query.methods.alter import AddUniqueConstraint, DropConstraint @dataclass class AddColumnClass: @@ -144,6 +145,8 @@ class MigrationManager: alter_columns: AlterColumnCollection = field( default_factory=AlterColumnCollection ) + add_unique_constraints: t.List[AddUniqueConstraint] = field(default_factory=list) + drop_unique_constraints: t.List[DropConstraint] = field(default_factory=list) raw: t.List[t.Union[t.Callable, t.Coroutine]] = field(default_factory=list) raw_backwards: t.List[t.Union[t.Callable, t.Coroutine]] = field( default_factory=list @@ -214,18 +217,30 @@ def add_column( if column_class is None: raise ValueError("Unrecognised column type") - cleaned_params = deserialise_params(params=params) - column = column_class(**cleaned_params) + if column_class is UniqueConstraint: + column = column_class(**params) + else: + cleaned_params = deserialise_params(params=params) + column = column_class(**cleaned_params) + column._meta.name = column_name column._meta.db_column_name = db_column_name - self.add_columns.append( - AddColumnClass( - column=column, - tablename=tablename, - table_class_name=table_class_name, + if isinstance(column_class,UniqueConstraint): + self.add_unique_constraints.append( + AddUniqueConstraint( + constraint_name=column_name, + columns=params.get('unique_columns') #type: ignore + ) + ) + else: + self.add_columns.append( + AddColumnClass( + column=column, + tablename=tablename, + table_class_name=table_class_name, + ) ) - ) def drop_column( self, @@ -233,13 +248,17 @@ def drop_column( tablename: str, column_name: str, db_column_name: t.Optional[str] = None, + column_class: t.Optional[t.Type[Column]] = None, ): + print(500000) + print(column_class) self.drop_columns.append( DropColumn( table_class_name=table_class_name, column_name=column_name, db_column_name=db_column_name or column_name, tablename=tablename, + column_class=column_class ) ) @@ -569,9 +588,17 @@ async def _run_drop_columns(self, backwards=False): ) for column in columns: - await _Table.alter().drop_column( - column=column.column_name - ).run() + print('============') + print(column.column_class) + print('============') + if column.column_class==UniqueConstraint: + await _Table.alter().drop_constraint( + constraint_name=column.db_column_name + ).run() + else: + await _Table.alter().drop_column( + column=column.column_name + ).run() async def _run_rename_tables(self, backwards=False): for rename_table in self.rename_tables: @@ -699,11 +726,17 @@ async def _run_add_columns(self, backwards=False): column = _Table._meta.get_column_by_name( add_column.column._meta.name ) - await _Table.alter().add_column( - name=column._meta.name, column=column - ).run() - if add_column.column._meta.index: - await _Table.create_index([add_column.column]).run() + if isinstance(add_column.column,UniqueConstraint): + await _Table.alter().add_unique_constraint( + add_column.column._meta.name, + add_column.column.unique_columns + ).run() + else: + await _Table.alter().add_column( + name=column._meta.name, column=column + ).run() + if add_column.column._meta.index: + await _Table.create_index([add_column.column]).run() async def run(self): print(f" - {self.migration_id} [forwards]... ", end="") diff --git a/piccolo/apps/migrations/auto/operations.py b/piccolo/apps/migrations/auto/operations.py index 5903e1000..d235a8605 100644 --- a/piccolo/apps/migrations/auto/operations.py +++ b/piccolo/apps/migrations/auto/operations.py @@ -40,6 +40,7 @@ class DropColumn: column_name: str db_column_name: str tablename: str + column_class: t.Optional[t.Type[Column]] = None @dataclass diff --git a/piccolo/apps/migrations/auto/schema_differ.py b/piccolo/apps/migrations/auto/schema_differ.py index 2427c44a5..6de24daf5 100644 --- a/piccolo/apps/migrations/auto/schema_differ.py +++ b/piccolo/apps/migrations/auto/schema_differ.py @@ -375,6 +375,7 @@ def alter_columns(self) -> AlterStatements: @property def drop_columns(self) -> AlterStatements: response = [] + extra_imports: t.List[Import] = [] for table in self.schema: snapshot_table = self._get_snapshot_table(table.class_name) if snapshot_table: @@ -388,11 +389,26 @@ def drop_columns(self) -> AlterStatements: in self.rename_columns_collection.old_column_names ): continue + column_class = column.column_class + extra_imports.append( + Import( + module=column_class.__module__, + target=column_class.__name__, #type: ignore + expect_conflict_with_global_name=getattr( + UniqueGlobalNames, + f"COLUMN_{column_class.__name__.upper()}", #type: ignore + None, + ), + ) + ) response.append( - f"manager.drop_column(table_class_name='{table.class_name}', tablename='{table.tablename}', column_name='{column.column_name}', db_column_name='{column.db_column_name}')" # noqa: E501 + f"manager.drop_column(table_class_name='{table.class_name}', tablename='{table.tablename}', column_name='{column.column_name}', db_column_name='{column.db_column_name}', column_class={column.column_class.__name__})" # noqa: E501 ) - return AlterStatements(statements=response) + return AlterStatements( + statements=response, + extra_imports=extra_imports + ) @property def add_columns(self) -> AlterStatements: diff --git a/piccolo/columns/constraints.py b/piccolo/columns/constraints.py new file mode 100644 index 000000000..e283e7af0 --- /dev/null +++ b/piccolo/columns/constraints.py @@ -0,0 +1,50 @@ +from .base import Selectable, ColumnMeta, Column + +class Constraint(Column): + def __init__(self) -> None: + pass + +class UniqueConstraint(Constraint): + def __init__(self, unique_columns: list[str]) -> None: + super().__init__() + + self._meta = ColumnMeta() + self.unique_columns = unique_columns + self._meta.params.update({ + 'unique_columns':self.unique_columns + }) + +#ALTER TABLE policies ADD CONSTRAINT constraint_test_1 UNIQUE (permission, role); +#ALTER TABLE policies DROP CONSTRAINT constraint_test_1; + @property + def column_type(self): + return "CONSTRAINT" + + @property + def ddl(self) -> str: + """ + Used when creating tables. + """ + unique_columns_string = ",".join(self.unique_columns) + query = f'"{self._meta.db_column_name}" {self.column_type} UNIQUE ({unique_columns_string})' + return query + + def get_select_string( + self, engine_type: str, with_alias: bool = True + ) -> str: + """ + How to refer to this column in a SQL query, taking account of any joins + and aliases. + """ + if with_alias: + if self._alias: + original_name = self._meta.get_full_name( + with_alias=False, + ) + return f'{original_name} AS "{self._alias}"' + else: + return self._meta.get_full_name( + with_alias=True, + ) + + return self._meta.get_full_name(with_alias=False) \ No newline at end of file diff --git a/piccolo/query/methods/alter.py b/piccolo/query/methods/alter.py index 115a35d0d..7c1e5f054 100644 --- a/piccolo/query/methods/alter.py +++ b/piccolo/query/methods/alter.py @@ -176,6 +176,17 @@ class SetLength(AlterColumnStatement): def ddl(self) -> str: return f'ALTER COLUMN "{self.column_name}" TYPE VARCHAR({self.length})' +@dataclass +class AddUniqueConstraint(AlterStatement): + __slots__ = ("constraint_name","columns") + + constraint_name: str + columns: list[str] + + @property + def ddl(self) -> str: + columns_str: str = ",".join(self.columns) + return f"ADD CONSTRAINT {self.constraint_name} UNIQUE ({columns_str})" @dataclass class DropConstraint(AlterStatement): @@ -264,6 +275,7 @@ class Alter(DDL): __slots__ = ( "_add_foreign_key_constraint", "_add", + "_add_unique_constraint", "_drop_constraint", "_drop_default", "_drop_table", @@ -282,6 +294,7 @@ def __init__(self, table: t.Type[Table], **kwargs): super().__init__(table, **kwargs) self._add_foreign_key_constraint: t.List[AddForeignKeyConstraint] = [] self._add: t.List[AddColumn] = [] + self._add_unique_constraint: t.List[AddUniqueConstraint] = [] self._drop_constraint: t.List[DropConstraint] = [] self._drop_default: t.List[DropDefault] = [] self._drop_table: t.Optional[DropTable] = None @@ -433,6 +446,12 @@ def _get_constraint_name(self, column: t.Union[str, ForeignKey]) -> str: tablename = self.table._meta.tablename return f"{tablename}_{column_name}_fk" + def add_unique_constraint(self, constraint_name: str, columns: list[str]): + self._add_unique_constraint.append( + AddUniqueConstraint(constraint_name=constraint_name,columns=columns) + ) + return self + def drop_constraint(self, constraint_name: str) -> Alter: self._drop_constraint.append( DropConstraint(constraint_name=constraint_name) @@ -520,6 +539,8 @@ def default_ddl(self) -> t.Sequence[str]: self._set_length, self._set_default, self._set_digits, + self._add_unique_constraint, + self._drop_constraint, ) ] diff --git a/piccolo/table.py b/piccolo/table.py index a792db105..fc189f93a 100644 --- a/piccolo/table.py +++ b/piccolo/table.py @@ -7,6 +7,7 @@ from dataclasses import dataclass, field from piccolo.columns import Column +from piccolo.columns.constraints import UniqueConstraint from piccolo.columns.column_types import ( JSON, JSONB, @@ -73,6 +74,7 @@ class TableMeta: primary_key: Column = field(default_factory=Column) json_columns: t.List[t.Union[JSON, JSONB]] = field(default_factory=list) secret_columns: t.List[Secret] = field(default_factory=list) + unique_constraints: t.List[UniqueConstraint] = field(default_factory=list) tags: t.List[str] = field(default_factory=list) help_text: t.Optional[str] = None _db: t.Optional[Engine] = None @@ -218,6 +220,7 @@ def __init_subclass__( json_columns: t.List[t.Union[JSON, JSONB]] = [] primary_key: t.Optional[Column] = None m2m_relationships: t.List[M2M] = [] + unique_constraints: t.List[UniqueConstraint] = [] attribute_names = itertools.chain( *[i.__dict__.keys() for i in reversed(cls.__mro__)] @@ -258,6 +261,9 @@ def __init_subclass__( if isinstance(column, (JSON, JSONB)): json_columns.append(column) + if isinstance(attribute, UniqueConstraint): + unique_constraints.append(attribute) + if isinstance(attribute, M2M): attribute._meta._name = attribute_name attribute._meta._table = cls @@ -279,6 +285,7 @@ def __init_subclass__( foreign_key_columns=foreign_key_columns, json_columns=json_columns, secret_columns=secret_columns, + unique_constraints=unique_constraints, tags=tags, help_text=help_text, _db=db, diff --git a/piccolo_conf.py b/piccolo_conf.py index a020d97b4..92c280cdf 100644 --- a/piccolo_conf.py +++ b/piccolo_conf.py @@ -11,10 +11,10 @@ from piccolo.conf.apps import AppRegistry from piccolo.engine.postgres import PostgresEngine - -DB = PostgresEngine(config={}) +connection_string: str = 'postgresql://test:test@127.0.0.1:5555/test' +DB = PostgresEngine(config={'dsn':connection_string}) # A list of paths to piccolo apps # e.g. ['blog.piccolo_app'] -APP_REGISTRY = AppRegistry(apps=["piccolo.apps.user.piccolo_app"]) +APP_REGISTRY = AppRegistry(apps=["piccolo.apps.user.piccolo_app","test_app.piccolo_app"]) diff --git a/test_app/__init__.py b/test_app/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test_app/piccolo_app.py b/test_app/piccolo_app.py new file mode 100644 index 000000000..59e80ca3a --- /dev/null +++ b/test_app/piccolo_app.py @@ -0,0 +1,22 @@ +""" +Import all of the Tables subclasses in your app here, and register them with +the APP_CONFIG. +""" + +import os + +from piccolo.conf.apps import AppConfig +from .tables import FooTable + +CURRENT_DIRECTORY = os.path.dirname(os.path.abspath(__file__)) + + +APP_CONFIG = AppConfig( + app_name="test_app", + migrations_folder_path=os.path.join( + CURRENT_DIRECTORY, "piccolo_migrations" + ), + table_classes=[FooTable], + migration_dependencies=[], + commands=[], +) diff --git a/test_app/piccolo_migrations/2022-08-10T15-10-37-698407.py b/test_app/piccolo_migrations/2022-08-10T15-10-37-698407.py new file mode 100644 index 000000000..0c63df14c --- /dev/null +++ b/test_app/piccolo_migrations/2022-08-10T15-10-37-698407.py @@ -0,0 +1,78 @@ +from piccolo.apps.migrations.auto.migration_manager import MigrationManager +from piccolo.columns.column_types import Text +from piccolo.columns.indexes import IndexMethod + + +ID = "2022-08-10T15:10:37:698407" +VERSION = "0.82.0" +DESCRIPTION = "" + + +async def forwards(): + manager = MigrationManager( + migration_id=ID, app_name="test_app", description=DESCRIPTION + ) + + manager.add_table("FooTable", tablename="foo_table") + + manager.add_column( + table_class_name="FooTable", + tablename="foo_table", + column_name="field_1", + db_column_name="field_1", + column_class_name="Text", + column_class=Text, + params={ + "default": "", + "null": False, + "primary_key": False, + "unique": False, + "index": False, + "index_method": IndexMethod.btree, + "choices": None, + "db_column_name": None, + "secret": False, + }, + ) + + manager.add_column( + table_class_name="FooTable", + tablename="foo_table", + column_name="field_2", + db_column_name="field_2", + column_class_name="Text", + column_class=Text, + params={ + "default": "", + "null": False, + "primary_key": False, + "unique": False, + "index": False, + "index_method": IndexMethod.btree, + "choices": None, + "db_column_name": None, + "secret": False, + }, + ) + + manager.add_column( + table_class_name="FooTable", + tablename="foo_table", + column_name="field_3", + db_column_name="field_3", + column_class_name="Text", + column_class=Text, + params={ + "default": "", + "null": False, + "primary_key": False, + "unique": False, + "index": False, + "index_method": IndexMethod.btree, + "choices": None, + "db_column_name": None, + "secret": False, + }, + ) + + return manager diff --git a/test_app/piccolo_migrations/2022-08-10T15-11-07-637432.py b/test_app/piccolo_migrations/2022-08-10T15-11-07-637432.py new file mode 100644 index 000000000..8b151d408 --- /dev/null +++ b/test_app/piccolo_migrations/2022-08-10T15-11-07-637432.py @@ -0,0 +1,25 @@ +from piccolo.apps.migrations.auto.migration_manager import MigrationManager +from piccolo.columns.constraints import UniqueConstraint + + +ID = "2022-08-10T15:11:07:637432" +VERSION = "0.82.0" +DESCRIPTION = "" + + +async def forwards(): + manager = MigrationManager( + migration_id=ID, app_name="test_app", description=DESCRIPTION + ) + + manager.add_column( + table_class_name="FooTable", + tablename="foo_table", + column_name="my_test_constraint_1", + db_column_name="my_test_constraint_1", + column_class_name="UniqueConstraint", + column_class=UniqueConstraint, + params={"unique_columns": ["field_1", "field_2"]}, + ) + + return manager diff --git a/test_app/piccolo_migrations/2022-08-10T16-19-25-729836.py b/test_app/piccolo_migrations/2022-08-10T16-19-25-729836.py new file mode 100644 index 000000000..34289e3f9 --- /dev/null +++ b/test_app/piccolo_migrations/2022-08-10T16-19-25-729836.py @@ -0,0 +1,23 @@ +from piccolo.apps.migrations.auto.migration_manager import MigrationManager +from piccolo.columns.constraints import UniqueConstraint + + +ID = "2022-08-10T16:19:25:729836" +VERSION = "0.82.0" +DESCRIPTION = "" + + +async def forwards(): + manager = MigrationManager( + migration_id=ID, app_name="test_app", description=DESCRIPTION + ) + + manager.drop_column( + table_class_name="FooTable", + tablename="foo_table", + column_name="my_test_constraint_1", + db_column_name="my_test_constraint_1", + column_class=UniqueConstraint, + ) + + return manager diff --git a/test_app/piccolo_migrations/__init__.py b/test_app/piccolo_migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test_app/tables.py b/test_app/tables.py new file mode 100644 index 000000000..5a2f7f1f8 --- /dev/null +++ b/test_app/tables.py @@ -0,0 +1,11 @@ +from piccolo.table import Table +from piccolo.columns import Text +from piccolo.columns.constraints import UniqueConstraint + +class FooTable(Table): + field_1 = Text() + field_2 = Text() + field_3 = Text() + + #my_test_constraint_1 = UniqueConstraint(['field_1','field_2']) + From 8ca2f1929f75351baad030cc4dc7a8c115bfe763 Mon Sep 17 00:00:00 2001 From: romanov Date: Wed, 10 Aug 2022 16:43:38 +0300 Subject: [PATCH 2/6] dev cmt --- .../apps/migrations/auto/migration_manager.py | 3 --- piccolo/apps/migrations/auto/schema_differ.py | 4 +++ .../2022-08-10T15-11-07-637432.py | 25 ------------------- .../2022-08-10T16-19-25-729836.py | 23 ----------------- ...98407.py => 2022-08-10T16-39-54-190612.py} | 13 +++++++++- test_app/tables.py | 2 +- 6 files changed, 17 insertions(+), 53 deletions(-) delete mode 100644 test_app/piccolo_migrations/2022-08-10T15-11-07-637432.py delete mode 100644 test_app/piccolo_migrations/2022-08-10T16-19-25-729836.py rename test_app/piccolo_migrations/{2022-08-10T15-10-37-698407.py => 2022-08-10T16-39-54-190612.py} (82%) diff --git a/piccolo/apps/migrations/auto/migration_manager.py b/piccolo/apps/migrations/auto/migration_manager.py index 2ddf5d17e..56d7d90cd 100644 --- a/piccolo/apps/migrations/auto/migration_manager.py +++ b/piccolo/apps/migrations/auto/migration_manager.py @@ -588,9 +588,6 @@ async def _run_drop_columns(self, backwards=False): ) for column in columns: - print('============') - print(column.column_class) - print('============') if column.column_class==UniqueConstraint: await _Table.alter().drop_constraint( constraint_name=column.db_column_name diff --git a/piccolo/apps/migrations/auto/schema_differ.py b/piccolo/apps/migrations/auto/schema_differ.py index 6de24daf5..333d43df8 100644 --- a/piccolo/apps/migrations/auto/schema_differ.py +++ b/piccolo/apps/migrations/auto/schema_differ.py @@ -15,6 +15,7 @@ UniqueGlobalNames, serialise_params, ) +from piccolo.columns.constraints import UniqueConstraint from piccolo.utils.printing import get_fixed_length_string @@ -351,6 +352,9 @@ def alter_columns(self) -> AlterStatements: ) if alter_column.old_column_class is not None: + if alter_column.old_column_class==UniqueConstraint: + print('You cannot ALTER UniqueConstraint! At first, delete it, then create the new one') + continue extra_imports.append( Import( module=alter_column.old_column_class.__module__, diff --git a/test_app/piccolo_migrations/2022-08-10T15-11-07-637432.py b/test_app/piccolo_migrations/2022-08-10T15-11-07-637432.py deleted file mode 100644 index 8b151d408..000000000 --- a/test_app/piccolo_migrations/2022-08-10T15-11-07-637432.py +++ /dev/null @@ -1,25 +0,0 @@ -from piccolo.apps.migrations.auto.migration_manager import MigrationManager -from piccolo.columns.constraints import UniqueConstraint - - -ID = "2022-08-10T15:11:07:637432" -VERSION = "0.82.0" -DESCRIPTION = "" - - -async def forwards(): - manager = MigrationManager( - migration_id=ID, app_name="test_app", description=DESCRIPTION - ) - - manager.add_column( - table_class_name="FooTable", - tablename="foo_table", - column_name="my_test_constraint_1", - db_column_name="my_test_constraint_1", - column_class_name="UniqueConstraint", - column_class=UniqueConstraint, - params={"unique_columns": ["field_1", "field_2"]}, - ) - - return manager diff --git a/test_app/piccolo_migrations/2022-08-10T16-19-25-729836.py b/test_app/piccolo_migrations/2022-08-10T16-19-25-729836.py deleted file mode 100644 index 34289e3f9..000000000 --- a/test_app/piccolo_migrations/2022-08-10T16-19-25-729836.py +++ /dev/null @@ -1,23 +0,0 @@ -from piccolo.apps.migrations.auto.migration_manager import MigrationManager -from piccolo.columns.constraints import UniqueConstraint - - -ID = "2022-08-10T16:19:25:729836" -VERSION = "0.82.0" -DESCRIPTION = "" - - -async def forwards(): - manager = MigrationManager( - migration_id=ID, app_name="test_app", description=DESCRIPTION - ) - - manager.drop_column( - table_class_name="FooTable", - tablename="foo_table", - column_name="my_test_constraint_1", - db_column_name="my_test_constraint_1", - column_class=UniqueConstraint, - ) - - return manager diff --git a/test_app/piccolo_migrations/2022-08-10T15-10-37-698407.py b/test_app/piccolo_migrations/2022-08-10T16-39-54-190612.py similarity index 82% rename from test_app/piccolo_migrations/2022-08-10T15-10-37-698407.py rename to test_app/piccolo_migrations/2022-08-10T16-39-54-190612.py index 0c63df14c..161b6d976 100644 --- a/test_app/piccolo_migrations/2022-08-10T15-10-37-698407.py +++ b/test_app/piccolo_migrations/2022-08-10T16-39-54-190612.py @@ -1,9 +1,10 @@ from piccolo.apps.migrations.auto.migration_manager import MigrationManager from piccolo.columns.column_types import Text +from piccolo.columns.constraints import UniqueConstraint from piccolo.columns.indexes import IndexMethod -ID = "2022-08-10T15:10:37:698407" +ID = "2022-08-10T16:39:54:190612" VERSION = "0.82.0" DESCRIPTION = "" @@ -75,4 +76,14 @@ async def forwards(): }, ) + manager.add_column( + table_class_name="FooTable", + tablename="foo_table", + column_name="my_test_constraint_1", + db_column_name="my_test_constraint_1", + column_class_name="UniqueConstraint", + column_class=UniqueConstraint, + params={"unique_columns": ["field_1", "field_2"]}, + ) + return manager diff --git a/test_app/tables.py b/test_app/tables.py index 5a2f7f1f8..49bff100f 100644 --- a/test_app/tables.py +++ b/test_app/tables.py @@ -7,5 +7,5 @@ class FooTable(Table): field_2 = Text() field_3 = Text() - #my_test_constraint_1 = UniqueConstraint(['field_1','field_2']) + my_test_constraint_1 = UniqueConstraint(['field_1','field_2']) From ff73ee7ecbc1c5d394f13e06470dbbe6bb3e94f5 Mon Sep 17 00:00:00 2001 From: romanov Date: Wed, 10 Aug 2022 16:52:54 +0300 Subject: [PATCH 3/6] CREATE ddl fix --- piccolo/columns/constraints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/piccolo/columns/constraints.py b/piccolo/columns/constraints.py index e283e7af0..f9bd90034 100644 --- a/piccolo/columns/constraints.py +++ b/piccolo/columns/constraints.py @@ -26,7 +26,7 @@ def ddl(self) -> str: Used when creating tables. """ unique_columns_string = ",".join(self.unique_columns) - query = f'"{self._meta.db_column_name}" {self.column_type} UNIQUE ({unique_columns_string})' + query = f'{self.column_type} "{self._meta.db_column_name}" UNIQUE ({unique_columns_string})' return query def get_select_string( From a08625634d131f9124e1cd99254ad3b19345b430 Mon Sep 17 00:00:00 2001 From: romanov Date: Thu, 11 Aug 2022 11:05:19 +0300 Subject: [PATCH 4/6] Docstring and tests correction --- .gitignore | 2 +- cli.py | 143 ------------------ .../apps/migrations/auto/migration_manager.py | 2 - piccolo/columns/constraints.py | 42 +++-- piccolo_conf.py | 9 +- test_app/__init__.py | 0 test_app/piccolo_app.py | 22 --- .../2022-08-10T16-39-54-190612.py | 89 ----------- test_app/piccolo_migrations/__init__.py | 0 test_app/tables.py | 11 -- .../migrations/auto/test_schema_differ.py | 6 +- 11 files changed, 24 insertions(+), 302 deletions(-) delete mode 100644 cli.py delete mode 100644 test_app/__init__.py delete mode 100644 test_app/piccolo_app.py delete mode 100644 test_app/piccolo_migrations/2022-08-10T16-39-54-190612.py delete mode 100644 test_app/piccolo_migrations/__init__.py delete mode 100644 test_app/tables.py diff --git a/.gitignore b/.gitignore index c14d1f2f0..028273765 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,4 @@ htmlcov/ prof/ .env/ .venv/ -result.json +result.json \ No newline at end of file diff --git a/cli.py b/cli.py deleted file mode 100644 index a175ddea4..000000000 --- a/cli.py +++ /dev/null @@ -1,143 +0,0 @@ -import os -import sys - -from targ import CLI - -try: - import uvloop # type: ignore - - uvloop.install() -except ImportError: - pass - -from piccolo.apps.app.piccolo_app import APP_CONFIG as app_config -from piccolo.apps.asgi.piccolo_app import APP_CONFIG as asgi_config -from piccolo.apps.fixtures.piccolo_app import APP_CONFIG as fixtures_config -from piccolo.apps.meta.piccolo_app import APP_CONFIG as meta_config -from piccolo.apps.migrations.commands.check import CheckMigrationManager -from piccolo.apps.migrations.piccolo_app import APP_CONFIG as migrations_config -from piccolo.apps.playground.piccolo_app import APP_CONFIG as playground_config -from piccolo.apps.project.piccolo_app import APP_CONFIG as project_config -from piccolo.apps.schema.piccolo_app import APP_CONFIG as schema_config -from piccolo.apps.shell.piccolo_app import APP_CONFIG as shell_config -from piccolo.apps.sql_shell.piccolo_app import APP_CONFIG as sql_shell_config -from piccolo.apps.tester.piccolo_app import APP_CONFIG as tester_config -from piccolo.apps.user.piccolo_app import APP_CONFIG as user_config -from piccolo.conf.apps import AppRegistry, Finder -from piccolo.utils.sync import run_sync -from piccolo.utils.warnings import Level, colored_warning - -DIAGNOSE_FLAG = "--diagnose" - - -def get_diagnose_flag() -> bool: - return DIAGNOSE_FLAG in sys.argv - - -def main(): - """ - The entrypoint to the Piccolo CLI. - """ - # In case it's run from an entrypoint: - sys.path.insert(0, os.getcwd()) - - ########################################################################### - # Run in diagnose mode if requested. - - diagnose = get_diagnose_flag() - if diagnose: - print("Diagnosis...") - if Finder(diagnose=True).get_app_registry(): - print("Everything OK") - return - - ########################################################################### - - cli = CLI(description="Piccolo CLI") - - ########################################################################### - # Register the base apps. - - for _app_config in [ - app_config, - asgi_config, - fixtures_config, - meta_config, - migrations_config, - playground_config, - project_config, - schema_config, - shell_config, - sql_shell_config, - tester_config, - user_config, - ]: - for command in _app_config.commands: - cli.register( - command.callable, - group_name=_app_config.app_name, - aliases=command.aliases, - ) - - ########################################################################### - # Get user defined apps. - - try: - APP_REGISTRY: AppRegistry = Finder().get_app_registry() - except (ImportError, AttributeError): - print( - "Can't import the APP_REGISTRY from piccolo_conf - some " - "commands may be missing. If this is a new project don't worry. " - f"To see a full traceback use `piccolo {DIAGNOSE_FLAG}`" - ) - else: - for app_name, _app_config in APP_REGISTRY.app_configs.items(): - for command in _app_config.commands: - if cli.command_exists( - group_name=app_name, command_name=command.callable.__name__ - ): - # Skipping - already registered. - continue - cli.register( - command.callable, - group_name=app_name, - aliases=command.aliases, - ) - - if "migrations" not in sys.argv: - # Show a warning if any migrations haven't been run. - # Don't run it if it looks like the user is running a migration - # command, as this information is redundant. - - try: - havent_ran_count = run_sync( - CheckMigrationManager(app_name="all").havent_ran_count() - ) - if havent_ran_count: - message = ( - f"{havent_ran_count} migration hasn't" - if havent_ran_count == 1 - else f"{havent_ran_count} migrations haven't" - ) - - colored_warning( - message=( - "=> {} been run - the app " - "might not behave as expected.\n" - "To check which use:\n" - " piccolo migrations check\n" - "To run all migrations:\n" - " piccolo migrations forwards all\n" - ).format(message), - level=Level.high, - ) - except Exception: - pass - - ########################################################################### - - cli.run() - - -if __name__ == "__main__": - main() diff --git a/piccolo/apps/migrations/auto/migration_manager.py b/piccolo/apps/migrations/auto/migration_manager.py index 56d7d90cd..30a52a65f 100644 --- a/piccolo/apps/migrations/auto/migration_manager.py +++ b/piccolo/apps/migrations/auto/migration_manager.py @@ -250,8 +250,6 @@ def drop_column( db_column_name: t.Optional[str] = None, column_class: t.Optional[t.Type[Column]] = None, ): - print(500000) - print(column_class) self.drop_columns.append( DropColumn( table_class_name=table_class_name, diff --git a/piccolo/columns/constraints.py b/piccolo/columns/constraints.py index f9bd90034..332b29245 100644 --- a/piccolo/columns/constraints.py +++ b/piccolo/columns/constraints.py @@ -1,21 +1,33 @@ -from .base import Selectable, ColumnMeta, Column +from .base import ColumnMeta, Column class Constraint(Column): def __init__(self) -> None: pass class UniqueConstraint(Constraint): + """ + This class represents UNIQUE CONSTRAINT, which can be use to create + many complex (multi-field) constraints for the Table + All manipulations with the Constraint are like anything about Columns + + Usage: + class FooTable(Table): + foo_field = Text() + bar_field = Text() + my_constraint_1 = UniqueConstraint(['foo_field','bar_field']) + + SQL queries for creating and dropping constrains are similar to: + ALTER TABLE foo_table ADD CONSTRAINT my_constraint_1 UNIQUE (foo_field, bar_field); + ALTER TABLE foo_table DROP IF EXIST CONSTRAINT my_constraint_1; + """ def __init__(self, unique_columns: list[str]) -> None: super().__init__() - self._meta = ColumnMeta() self.unique_columns = unique_columns self._meta.params.update({ 'unique_columns':self.unique_columns }) -#ALTER TABLE policies ADD CONSTRAINT constraint_test_1 UNIQUE (permission, role); -#ALTER TABLE policies DROP CONSTRAINT constraint_test_1; @property def column_type(self): return "CONSTRAINT" @@ -27,24 +39,4 @@ def ddl(self) -> str: """ unique_columns_string = ",".join(self.unique_columns) query = f'{self.column_type} "{self._meta.db_column_name}" UNIQUE ({unique_columns_string})' - return query - - def get_select_string( - self, engine_type: str, with_alias: bool = True - ) -> str: - """ - How to refer to this column in a SQL query, taking account of any joins - and aliases. - """ - if with_alias: - if self._alias: - original_name = self._meta.get_full_name( - with_alias=False, - ) - return f'{original_name} AS "{self._alias}"' - else: - return self._meta.get_full_name( - with_alias=True, - ) - - return self._meta.get_full_name(with_alias=False) \ No newline at end of file + return query \ No newline at end of file diff --git a/piccolo_conf.py b/piccolo_conf.py index 92c280cdf..26167a189 100644 --- a/piccolo_conf.py +++ b/piccolo_conf.py @@ -1,20 +1,17 @@ """ This piccolo_conf file is just here so migrations can be made for Piccolo's own internal apps. - For example: - python -m piccolo.main migration new user --auto - """ from piccolo.conf.apps import AppRegistry from piccolo.engine.postgres import PostgresEngine -connection_string: str = 'postgresql://test:test@127.0.0.1:5555/test' -DB = PostgresEngine(config={'dsn':connection_string}) + +DB = PostgresEngine(config={}) # A list of paths to piccolo apps # e.g. ['blog.piccolo_app'] -APP_REGISTRY = AppRegistry(apps=["piccolo.apps.user.piccolo_app","test_app.piccolo_app"]) +APP_REGISTRY = AppRegistry(apps=["piccolo.apps.user.piccolo_app"]) \ No newline at end of file diff --git a/test_app/__init__.py b/test_app/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/test_app/piccolo_app.py b/test_app/piccolo_app.py deleted file mode 100644 index 59e80ca3a..000000000 --- a/test_app/piccolo_app.py +++ /dev/null @@ -1,22 +0,0 @@ -""" -Import all of the Tables subclasses in your app here, and register them with -the APP_CONFIG. -""" - -import os - -from piccolo.conf.apps import AppConfig -from .tables import FooTable - -CURRENT_DIRECTORY = os.path.dirname(os.path.abspath(__file__)) - - -APP_CONFIG = AppConfig( - app_name="test_app", - migrations_folder_path=os.path.join( - CURRENT_DIRECTORY, "piccolo_migrations" - ), - table_classes=[FooTable], - migration_dependencies=[], - commands=[], -) diff --git a/test_app/piccolo_migrations/2022-08-10T16-39-54-190612.py b/test_app/piccolo_migrations/2022-08-10T16-39-54-190612.py deleted file mode 100644 index 161b6d976..000000000 --- a/test_app/piccolo_migrations/2022-08-10T16-39-54-190612.py +++ /dev/null @@ -1,89 +0,0 @@ -from piccolo.apps.migrations.auto.migration_manager import MigrationManager -from piccolo.columns.column_types import Text -from piccolo.columns.constraints import UniqueConstraint -from piccolo.columns.indexes import IndexMethod - - -ID = "2022-08-10T16:39:54:190612" -VERSION = "0.82.0" -DESCRIPTION = "" - - -async def forwards(): - manager = MigrationManager( - migration_id=ID, app_name="test_app", description=DESCRIPTION - ) - - manager.add_table("FooTable", tablename="foo_table") - - manager.add_column( - table_class_name="FooTable", - tablename="foo_table", - column_name="field_1", - db_column_name="field_1", - column_class_name="Text", - column_class=Text, - params={ - "default": "", - "null": False, - "primary_key": False, - "unique": False, - "index": False, - "index_method": IndexMethod.btree, - "choices": None, - "db_column_name": None, - "secret": False, - }, - ) - - manager.add_column( - table_class_name="FooTable", - tablename="foo_table", - column_name="field_2", - db_column_name="field_2", - column_class_name="Text", - column_class=Text, - params={ - "default": "", - "null": False, - "primary_key": False, - "unique": False, - "index": False, - "index_method": IndexMethod.btree, - "choices": None, - "db_column_name": None, - "secret": False, - }, - ) - - manager.add_column( - table_class_name="FooTable", - tablename="foo_table", - column_name="field_3", - db_column_name="field_3", - column_class_name="Text", - column_class=Text, - params={ - "default": "", - "null": False, - "primary_key": False, - "unique": False, - "index": False, - "index_method": IndexMethod.btree, - "choices": None, - "db_column_name": None, - "secret": False, - }, - ) - - manager.add_column( - table_class_name="FooTable", - tablename="foo_table", - column_name="my_test_constraint_1", - db_column_name="my_test_constraint_1", - column_class_name="UniqueConstraint", - column_class=UniqueConstraint, - params={"unique_columns": ["field_1", "field_2"]}, - ) - - return manager diff --git a/test_app/piccolo_migrations/__init__.py b/test_app/piccolo_migrations/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/test_app/tables.py b/test_app/tables.py deleted file mode 100644 index 49bff100f..000000000 --- a/test_app/tables.py +++ /dev/null @@ -1,11 +0,0 @@ -from piccolo.table import Table -from piccolo.columns import Text -from piccolo.columns.constraints import UniqueConstraint - -class FooTable(Table): - field_1 = Text() - field_2 = Text() - field_3 = Text() - - my_test_constraint_1 = UniqueConstraint(['field_1','field_2']) - diff --git a/tests/apps/migrations/auto/test_schema_differ.py b/tests/apps/migrations/auto/test_schema_differ.py index 6fccff2e5..58fc2aeb0 100644 --- a/tests/apps/migrations/auto/test_schema_differ.py +++ b/tests/apps/migrations/auto/test_schema_differ.py @@ -158,7 +158,7 @@ def test_drop_column(self): self.assertTrue(len(schema_differ.drop_columns.statements) == 1) self.assertEqual( schema_differ.drop_columns.statements[0], - "manager.drop_column(table_class_name='Band', tablename='band', column_name='genre', db_column_name='genre')", # noqa + "manager.drop_column(table_class_name='Band', tablename='band', column_name='genre', db_column_name='genre', column_class=Varchar)", # noqa ) def test_rename_column(self): @@ -213,7 +213,7 @@ def test_rename_column(self): self.assertEqual( schema_differ.drop_columns.statements, [ - "manager.drop_column(table_class_name='Band', tablename='band', column_name='title', db_column_name='title')" # noqa: E501 + "manager.drop_column(table_class_name='Band', tablename='band', column_name='title', db_column_name='title', column_class=Varchar)" # noqa: E501 ], ) self.assertTrue(schema_differ.rename_columns.statements == []) @@ -355,7 +355,7 @@ def mock_input(value: str): self.assertEqual( schema_differ.drop_columns.statements, [ - "manager.drop_column(table_class_name='Band', tablename='band', column_name='b1', db_column_name='b1')" # noqa: E501 + "manager.drop_column(table_class_name='Band', tablename='band', column_name='b1', db_column_name='b1', column_class=Varchar)" # noqa: E501 ], ) self.assertEqual( From 4851214958adbb516180ca2abdb7324274805354 Mon Sep 17 00:00:00 2001 From: romanov Date: Thu, 11 Aug 2022 11:12:15 +0300 Subject: [PATCH 5/6] Garbage delete --- main.py | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 main.py diff --git a/main.py b/main.py deleted file mode 100644 index 4d5254987..000000000 --- a/main.py +++ /dev/null @@ -1,13 +0,0 @@ -from piccolo.engine.postgres import PostgresEngine -from piccolo.table import Table -from piccolo.columns import ForeignKey, Integer, Varchar - - -# connection_string: str = 'postgresql://test:test@127.0.0.1:5555/test' -# engine = PostgresEngine(config={'dsn':connection_string}) - - -# class Band(Table): -# name = Varchar(length=100) -# popularity = Integer() - From dc60c23d73948ba18318eb8e1eb813d3bb3d8c21 Mon Sep 17 00:00:00 2001 From: northpowered Date: Mon, 6 Mar 2023 13:33:42 +0300 Subject: [PATCH 6/6] Adoptation for py3.7 and piccolo v.0.109.0 --- .gitignore | 3 +-- piccolo/__init__.py | 2 +- piccolo/columns/constraints.py | 3 ++- piccolo/query/methods/alter.py | 4 ++-- piccolo/table.py | 1 + 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 028273765..59f7590ba 100644 --- a/.gitignore +++ b/.gitignore @@ -17,5 +17,4 @@ coverage.xml htmlcov/ prof/ .env/ -.venv/ -result.json \ No newline at end of file +.venv/ \ No newline at end of file diff --git a/piccolo/__init__.py b/piccolo/__init__.py index 211046fd7..5c95ea19e 100644 --- a/piccolo/__init__.py +++ b/piccolo/__init__.py @@ -1 +1 @@ -__VERSION__ = "0.82.0" \ No newline at end of file +__VERSION__ = "0.110.0" \ No newline at end of file diff --git a/piccolo/columns/constraints.py b/piccolo/columns/constraints.py index 332b29245..79995fa08 100644 --- a/piccolo/columns/constraints.py +++ b/piccolo/columns/constraints.py @@ -1,4 +1,5 @@ from .base import ColumnMeta, Column +import typing as t class Constraint(Column): def __init__(self) -> None: @@ -20,7 +21,7 @@ class FooTable(Table): ALTER TABLE foo_table ADD CONSTRAINT my_constraint_1 UNIQUE (foo_field, bar_field); ALTER TABLE foo_table DROP IF EXIST CONSTRAINT my_constraint_1; """ - def __init__(self, unique_columns: list[str]) -> None: + def __init__(self, unique_columns: t.List[str]) -> None: super().__init__() self._meta = ColumnMeta() self.unique_columns = unique_columns diff --git a/piccolo/query/methods/alter.py b/piccolo/query/methods/alter.py index 7c1e5f054..93ecfd11e 100644 --- a/piccolo/query/methods/alter.py +++ b/piccolo/query/methods/alter.py @@ -181,7 +181,7 @@ class AddUniqueConstraint(AlterStatement): __slots__ = ("constraint_name","columns") constraint_name: str - columns: list[str] + columns: t.List[str] @property def ddl(self) -> str: @@ -446,7 +446,7 @@ def _get_constraint_name(self, column: t.Union[str, ForeignKey]) -> str: tablename = self.table._meta.tablename return f"{tablename}_{column_name}_fk" - def add_unique_constraint(self, constraint_name: str, columns: list[str]): + def add_unique_constraint(self, constraint_name: str, columns: t.List[str]): self._add_unique_constraint.append( AddUniqueConstraint(constraint_name=constraint_name,columns=columns) ) diff --git a/piccolo/table.py b/piccolo/table.py index fc189f93a..8f23849a7 100644 --- a/piccolo/table.py +++ b/piccolo/table.py @@ -79,6 +79,7 @@ class TableMeta: help_text: t.Optional[str] = None _db: t.Optional[Engine] = None m2m_relationships: t.List[M2M] = field(default_factory=list) + auto_update_columns: t.List[Column] = field(default_factory=list) # Records reverse foreign key relationships - i.e. when the current table # is the target of a foreign key. Used by external libraries such as