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

feature: fixes #87 drop_unused_enums, detect_enum_values_changes feature flags that can be disabled #91

Merged
merged 2 commits into from
Dec 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 45 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,33 +30,19 @@ import alembic_postgresql_enum

To the top of your migrations/env.py file.

## Configuration

You can configure this extension to disable parts of it, or to enable some feature flags

To do so you need to call set_configuration function after the import:

```python
import alembic_postgresql_enum

alembic_postgresql_enum.set_configuration(
alembic_postgresql_enum.Config(
add_type_ignore=True,
)
)
```
## Features

* [Creation of enums](#creation-of-enum)
* [Deletion of unreferenced enums](#deletion-of-unreferenced-enum)
* [Creation of new enum values](#creation-of-new-enum-values)
* [Deletion of enums values](#deletion-of-enums-values)
* [Renaming of enum values](#rename-enum-value)
* [Detection of enum values changes](#detection-of-enum-values-changes)
* [Creation of new enum values](#creation-of-new-enum-values)
* [Deletion of enums values](#deletion-of-enums-values)
* [Renaming of enum values](#rename-enum-value)
* [Omitting managing enums](#omitting-managing-enums)

### Creation of enum<a id="creation-of-enum"></a>
## Creation of enum<a id="creation-of-enum"></a>

#### When table is created
### When table is created

```python
class MyEnum(enum.Enum):
Expand Down Expand Up @@ -93,7 +79,7 @@ def downgrade():
# ### end Alembic commands ###
```

#### When column is added
### When column is added
```python
class MyEnum(enum.Enum):
one = 1
Expand Down Expand Up @@ -125,7 +111,7 @@ def downgrade():
# ### end Alembic commands ###
```

### Deletion of unreferenced enum<a id="deletion-of-unreferenced-enum"></a>
## Deletion of unreferenced enum<a id="deletion-of-unreferenced-enum"></a>
If enum is defined in postgres schema, but its mentions removed from code - It will be automatically removed
```python
class ExampleTable(BaseModel):
Expand All @@ -148,6 +134,10 @@ def downgrade():
# ### end Alembic commands ###
```

## Detection of enum values changes<a id="detection-of-enum-values-changes"></a>

***Can be disabled with `detect_enum_values_changes` configuration flag turned off***

### Creation of new enum values<a id="creation-of-new-enum-values"></a>

If new enum value is defined sync_enum_values function call will be added to migration to account for it
Expand Down Expand Up @@ -221,6 +211,7 @@ def downgrade():
# ### end Alembic commands ###
```


### Rename enum value<a id="rename-enum-value"></a>
In this case you must manually edit migration

Expand Down Expand Up @@ -286,7 +277,7 @@ Do not forget to switch places old and new values for downgrade

All defaults in postgres will be renamed automatically as well

### Omitting managing enums<a id="omitting-managing-enums"></a>
## Omitting managing enums<a id="omitting-managing-enums"></a>

If configured `include_name` function returns `False` given enum will be not managed.
```python
Expand All @@ -302,4 +293,34 @@ alembic_postgresql_enum.set_configuration(
)
```

Feature is similar to [sqlalchemy feature for tables](https://alembic.sqlalchemy.org/en/latest/autogenerate.html#omitting-table-names-from-the-autogenerate-process)
Feature is similar to [sqlalchemy feature for tables](https://alembic.sqlalchemy.org/en/latest/autogenerate.html#omitting-table-names-from-the-autogenerate-process)

## Configuration

You can configure this extension to disable parts of it, or to enable some feature flags

To do so you need to call set_configuration function after the import:

```python
import alembic_postgresql_enum

alembic_postgresql_enum.set_configuration(
alembic_postgresql_enum.Config(
add_type_ignore=True,
)
)
```

Available options:

- `add_type_ignore` (`False` by default) - flag that can be turned on
to add `# type: ignore[attr-defined]` at the end of generated `op.sync_enum_values` calls.
This is helpful if you are using type checker such as `mypy`.
`type: ignore` is needed because there is no way to add new function to an existing alembic's `op`.

- `include_name` (`lambda _: True` bby default) - it adds ability to ignore process enum by name in similar way alembic allows to define `include_name` function.
This property accepts function that takes enum name and returns whether it should be processed.

- `drop_unused_enums` (`True` by default) - feature flag that can be turned off to disable clean up of undeclared enums

- `detect_enum_values_changes` (`True` by default) - feature flag that can be turned off to disable generation of `op.sync_enum_values`.
20 changes: 11 additions & 9 deletions alembic_postgresql_enum/compare_dispatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,14 @@ def compare_enums(

create_new_enums(definitions, declarations.enum_values, schema, upgrade_ops)

drop_unused_enums(definitions, declarations.enum_values, schema, upgrade_ops)

sync_changed_enums(
definitions,
declarations.enum_values,
declarations.enum_table_references,
schema,
upgrade_ops,
)
if configuration.drop_unused_enums:
drop_unused_enums(definitions, declarations.enum_values, schema, upgrade_ops)

if configuration.detect_enum_values_changes:
sync_changed_enums(
definitions,
declarations.enum_values,
declarations.enum_table_references,
schema,
upgrade_ops,
)
2 changes: 2 additions & 0 deletions alembic_postgresql_enum/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
class Config:
add_type_ignore: bool = False
include_name: Callable[[str], bool] = lambda _: True
drop_unused_enums: bool = True
detect_enum_values_changes: bool = True


_config = Config()
Expand Down
1 change: 0 additions & 1 deletion alembic_postgresql_enum/sql_commands/column_default.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ def rename_default_if_required(
return column_default_value

if default_value.endswith("[]"):

# remove old type postfix
column_default_value = default_value[: default_value.find("::")]

Expand Down
31 changes: 31 additions & 0 deletions tests/sync_enum_values/test_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,37 @@
from tests.utils.migration_context import create_migration_context


class TestDisabledSyncEnumValues(CompareAndRunTestCase):
"""Check that enum variants are updated when new variant is added"""

config = Config(detect_enum_values_changes=False)

old_enum_variants = ["active", "passive"]
new_enum_variants = old_enum_variants + ["banned"]

def get_database_schema(self) -> MetaData:
schema = get_schema_with_enum_variants(self.old_enum_variants)
return schema

def get_target_schema(self) -> MetaData:
schema = get_schema_with_enum_variants(self.new_enum_variants)
return schema

def get_expected_upgrade(self) -> str:
return f"""
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###
"""

def get_expected_downgrade(self) -> str:
return f"""
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###
"""


class TestAddNewEnumValueRender(CompareAndRunTestCase):
"""Check that enum variants are updated when new variant is added"""

Expand Down
31 changes: 31 additions & 0 deletions tests/test_enum_creation/test_drop_column.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import TYPE_CHECKING

from alembic_postgresql_enum import Config
from alembic_postgresql_enum.operations import DropEnumOp
from tests.base.run_migration_test_abc import CompareAndRunTestCase

Expand Down Expand Up @@ -63,6 +64,36 @@ def get_expected_downgrade(self) -> str:
"""


class TestDisabledDeleteEnumAfterDropColumn(CompareAndRunTestCase):
"""Check that library correctly removes unused enum after drop_column"""

config = Config(
drop_unused_enums=False,
)
enum_variants_to_delete = ["active", "passive"]

def get_database_schema(self) -> MetaData:
return get_schema_with_enum_variants(self.enum_variants_to_delete)

def get_target_schema(self) -> MetaData:
return get_schema_without_enum()

def get_expected_upgrade(self) -> str:
return f"""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('{USER_TABLE_NAME}', '{USER_STATUS_COLUMN_NAME}')
# ### end Alembic commands ###
"""

def get_expected_downgrade(self) -> str:
# For some reason alembic decided to add redundant autoincrement=False on downgrade
return f"""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('{USER_TABLE_NAME}', sa.Column('{USER_STATUS_COLUMN_NAME}', postgresql.ENUM({', '.join(map(repr, self.enum_variants_to_delete))}, name='{USER_STATUS_ENUM_NAME}', create_type=False), autoincrement=False, nullable=True))
# ### end Alembic commands ###
"""


class TestDeleteEnumAfterDropColumnWithArray(CompareAndRunTestCase):
"""Check that library correctly removes unused enum after drop_column. Enum is used in ARRAY"""

Expand Down
Loading