From 93d7d2f6d1afd4505765137922dee1b0b0df5052 Mon Sep 17 00:00:00 2001 From: "Ronny V." Date: Mon, 25 Nov 2024 20:00:49 +0100 Subject: [PATCH] v11.6.0 (#39) --- CHANGES.md | 3 +++ ambient_toolbox/__init__.py | 2 +- .../commands/detect_ghost_tables.py | 24 +++++++++++++++++++ docs/features/cleanup.md | 16 +++++++++++++ docs/index.rst | 1 + tests/management/__init__.py | 0 tests/management/commands/__init__.py | 0 .../commands/test_detect_ghost_tables.py | 22 +++++++++++++++++ tests/query_selectors/__init__.py | 0 9 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 ambient_toolbox/management/commands/detect_ghost_tables.py create mode 100644 docs/features/cleanup.md create mode 100644 tests/management/__init__.py create mode 100644 tests/management/commands/__init__.py create mode 100644 tests/management/commands/test_detect_ghost_tables.py create mode 100644 tests/query_selectors/__init__.py diff --git a/CHANGES.md b/CHANGES.md index fb280ba..88ff381 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,8 @@ # Changelog +**11.6.0** (2024-11-20) + * Added `detect_ghost_tables` management command + **11.5.5** (2024-11-20) * Moved permission check to "apps.ready()" method * Internal updates via `ambient-package-update` diff --git a/ambient_toolbox/__init__.py b/ambient_toolbox/__init__.py index 4a9feb4..f2e1b16 100644 --- a/ambient_toolbox/__init__.py +++ b/ambient_toolbox/__init__.py @@ -1,3 +1,3 @@ """Python toolbox of Ambient Digital containing an abundance of useful tools and gadgets.""" -__version__ = "11.5.5" +__version__ = "11.6.0" diff --git a/ambient_toolbox/management/commands/detect_ghost_tables.py b/ambient_toolbox/management/commands/detect_ghost_tables.py new file mode 100644 index 0000000..43ee5e3 --- /dev/null +++ b/ambient_toolbox/management/commands/detect_ghost_tables.py @@ -0,0 +1,24 @@ +import sys + +from django.core.management.base import BaseCommand +from django.db import connection + + +class Command(BaseCommand): + """ + Script to detect ghost tables. + Ghost tables are tables which were probably created by Django, + removed in the codebase but never removed in the database. + """ + + def handle(self, *args, **options): + table_names = set(connection.introspection.table_names()) + django_table_names = set(connection.introspection.django_table_names()) + possible_matches = table_names - django_table_names - {"django_migrations"} + + if len(possible_matches) == 0: + return + + sys.stdout.write("The following tables might be left-overs and can be deleted:\n") + for table in possible_matches: + sys.stdout.write(f"* {table}\n") diff --git a/docs/features/cleanup.md b/docs/features/cleanup.md new file mode 100644 index 0000000..707d235 --- /dev/null +++ b/docs/features/cleanup.md @@ -0,0 +1,16 @@ +# Clean-up + +## Detect "forgotten" tables + +Forgotten or ghost tables are tables which were created by Django migrations but then removed in the code. Django will +not necessarily remove tables when deleting models. Adam Johnson wrote +a [neat snippet](https://adamj.eu/tech/2024/11/21/django-tables-without-models/) to detect such tables. + +This package provides a management command to neatly list those tables in the command-line. + +```shell +python ./manage.py detect_ghost_tables + +> The following tables might be left-overs and can be deleted: +> * invalid_table +``` diff --git a/docs/index.rst b/docs/index.rst index a5d6b10..18e2c57 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -16,6 +16,7 @@ The package is published at pypi under the following link: `https://pypi.org/pro features/setup.md features/admin.md + features/cleanup.md features/context_manager.md features/context_processors.md features/database_anonymisation.md diff --git a/tests/management/__init__.py b/tests/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/management/commands/__init__.py b/tests/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/management/commands/test_detect_ghost_tables.py b/tests/management/commands/test_detect_ghost_tables.py new file mode 100644 index 0000000..52bf03d --- /dev/null +++ b/tests/management/commands/test_detect_ghost_tables.py @@ -0,0 +1,22 @@ +import sys +from unittest import mock + +from django.core.management import call_command +from django.db import connection +from django.test import SimpleTestCase + + +class DetectGhostTableCommandTest(SimpleTestCase): + @mock.patch.object(connection.introspection, "table_names", return_value=["invalid_table"]) + def test_management_command_match_found(self, *args): + with mock.patch.object(sys.stdout, "write") as mocked_write: + call_command("detect_ghost_tables") + + mocked_write.assert_called_with("* invalid_table\n") + + @mock.patch.object(connection.introspection, "table_names", return_value=[]) + def test_management_no_match_found(self, *args): + with mock.patch.object(sys.stdout, "write") as mocked_write: + call_command("detect_ghost_tables") + + mocked_write.assert_not_called() diff --git a/tests/query_selectors/__init__.py b/tests/query_selectors/__init__.py new file mode 100644 index 0000000..e69de29