-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'reset-modes' adding different reset modes during import
This introduces support for "reset modes" -- different approaches for ensuring a clean database before importing the fresh data. Initial support is for three modes: - `drop-database`: the default; drops the database & re-creates it. - `drop-tables`: drops the tables the Django codebase is aware of, useful if the Django database user doesn't have access to drop the entire database. - `none`: no attempt to reset the database, useful if the user has already manually configured the database or otherwise wants more control over setup. Using the second of these in a staging-type environment is the main motivation for this change. This also updates the existing `PostgresSequences` extra to cope with a sequence of a given name already being present by overwriting it. This doesn't feel like the best solution (the result if the import somehow lists the same sequence twice may be unexpected), but seemed probably the simplest for now given what devdata is typically used for. Fixes #23
- Loading branch information
Showing
9 changed files
with
499 additions
and
70 deletions.
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
""" | ||
Alternative implementations for ensuring a clean database before import. | ||
""" | ||
|
||
import abc | ||
|
||
from django.db import connections | ||
from django.db.migrations.recorder import MigrationRecorder | ||
|
||
from .settings import settings | ||
from .utils import nodb_cursor | ||
|
||
MODES = {} | ||
|
||
|
||
class Reset(abc.ABC): | ||
def __init_subclass__(cls) -> None: | ||
super().__init_subclass__() | ||
MODES[cls.slug] = cls() | ||
|
||
@property | ||
@abc.abstractclassmethod | ||
def slug(self) -> str: | ||
raise NotImplementedError | ||
|
||
@property | ||
@abc.abstractclassmethod | ||
def description_for_confirmation(self) -> str: | ||
raise NotImplementedError | ||
|
||
def reset_database(self, django_dbname: str) -> None: | ||
raise NotImplementedError | ||
|
||
def __str__(self) -> str: | ||
# Use the slug as the str for easier integration into the CLI | ||
return self.slug | ||
|
||
|
||
class DropDatabaseReset(Reset): | ||
""" | ||
Drop the entire database and re-create it using Django's test utils. | ||
This is suitable in cases where Django is configured with a database | ||
superuser account, which is likely to be the case in local development. | ||
""" | ||
|
||
slug = "drop-database" | ||
|
||
description_for_confirmation = "delete the database" | ||
|
||
def reset_database(self, django_dbname: str) -> None: | ||
db_conf = settings.DATABASES[django_dbname] | ||
pg_dbname = db_conf["NAME"] | ||
|
||
connection = connections[django_dbname] | ||
|
||
with nodb_cursor(connection) as cursor: | ||
cursor.execute("DROP DATABASE IF EXISTS {}".format(pg_dbname)) | ||
|
||
creator = connection.creation | ||
creator._execute_create_test_db( | ||
cursor, | ||
{ | ||
"dbname": pg_dbname, | ||
"suffix": creator.sql_table_creation_suffix(), | ||
}, | ||
) | ||
|
||
|
||
class DropTablesReset(Reset): | ||
""" | ||
Drop all the tables which Django knows about, including migration history. | ||
This is suitable in cases where the current state of the database can be | ||
assumed to be similar enough to the new state that removing the tables alone | ||
is sufficient to clear out the data. For databases which support other forms | ||
of data (e.g: Postgres sequences decoupled from tables) this mode will not | ||
touch those data and the user must ensure they are handled suitably. | ||
This is expected to be useful in cases where Django is configured with | ||
administrative privileges within a database, but may not have access to drop | ||
the entire database. | ||
Note: this will not touch other database entities (e.g: Postgres sequences & | ||
views) which may be present but are not managed by Django models -- even if | ||
they were created by running migrations (e.g: via `RunSQL`). | ||
""" | ||
|
||
slug = "drop-tables" | ||
|
||
description_for_confirmation = "delete all tables in the database" | ||
|
||
def reset_database(self, django_dbname: str) -> None: | ||
connection = connections[django_dbname] | ||
|
||
with connection.cursor() as cursor: | ||
table_names = connection.introspection.table_names(cursor) | ||
|
||
models = connection.introspection.installed_models(table_names) | ||
|
||
if MigrationRecorder(connection).has_table(): | ||
models.add(MigrationRecorder.Migration) | ||
|
||
with connection.schema_editor() as editor: | ||
for model in models: | ||
editor.delete_model(model) | ||
|
||
|
||
class NoReset(Reset): | ||
""" | ||
Perform no resetting against the database. | ||
This is suitable in cases where the user has already manually configured the | ||
target database or otherwise wants more control over the setup. The user is | ||
responsible for ensuring that the database is in a state ready to have the | ||
schema migrated into it. | ||
Notes: | ||
* As `loaddata` is used to import data, this mode may result in a merging | ||
of the new and existing data (if there is any). | ||
* Django's migrations table does not have any uniqueness constraints, | ||
meaning that even identical rows may be reinserted and resulting in | ||
apparently duplicate rows in that table. The effects of this on Django | ||
are unknown. You have been warned. | ||
""" | ||
|
||
slug = "none" | ||
|
||
description_for_confirmation = "merge into the existing database" | ||
|
||
def reset_database(self, django_dbname: str) -> None: | ||
pass |
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
Oops, something went wrong.