Skip to content

Commit

Permalink
Adding "connection-name" optional parameter (#15)
Browse files Browse the repository at this point in the history
* Adding "connection-name" optional parameter
Updating on poetry installation steps, README.md, and requirements to support Django 1.11+
* Changes from feedback: correction of source for DEFAULT_DB_ALIAS
Additional tweaks to provide convenient access to the connection_name parameter
Setting min Django version to 1.11.17 since that's the first version supporting Python 3.7
* Bump version to 0.2.0
* Update poetry.lock
  • Loading branch information
bluedenim authored Apr 2, 2023
1 parent cd64a34 commit 2c2a058
Show file tree
Hide file tree
Showing 13 changed files with 508 additions and 367 deletions.
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
FROM python:3
FROM python:3.9

WORKDIR /code

RUN pip install --upgrade pip

# Install and setup Poetry
RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python -
RUN echo export PATH=\$PATH:\$HOME/.poetry/bin >> /root/.bashrc
RUN curl -sSL https://install.python-poetry.org | python3 -
RUN echo export PATH=\$PATH:\$HOME/.local/bin >> /root/.bashrc
16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@ Django commands to help with running Django migrations.

Shows existing migration records in your `django_migration` table.

#### Optional parameters:

* `--format (console | csv)` print the info in CSV or friendlier console format (default)

```
> python manage.py migration_records --format csv
ID,Applied,App,Name
Expand All @@ -50,6 +46,10 @@ These are the records of migrations applied. The fields indicate:
* App - name of the Django app
* Name - name of the migration

#### Optional parameters:

* `--format (console | csv)` print the info in CSV or friendlier console format (default)
* `--connection-name {connection}` the connection name to use (default is `django.db.DEFAULT_DB_ALIAS`)

### migration_current_id

Expand All @@ -62,6 +62,10 @@ Shows the ID of the latest migration record in your `django_migration` table.

192 is the ID of the latest record as shown above.

#### Optional parameters:

* `--connection-name {connection}` the connection name to use (default is `django.db.DEFAULT_DB_ALIAS`)

### migration_rollback

Roll-back (unapply) previously applied migrations _after_ (but not including) the migration ID provided.
Expand Down Expand Up @@ -116,6 +120,7 @@ Running migrations:

#### Optional parameters:

* `--connection-name {connection}` the connection name to use (default is `django.db.DEFAULT_DB_ALIAS`)
* `--dry-run` will print the commands but will not actually run them
* `--migrate-cmd <command to run migrations>` sets the command to run migrations with. The command must accept
the app and migration name as the `{app}` and `{name}` placeholders, respectively.
Expand Down Expand Up @@ -162,9 +167,8 @@ To delete without confirmation, use the `--yes` option:
python manage.py migration_delete myapp 0003_some_migration --yes
```


#### Optional parameters:

* `--connection-name {connection}` the connection name to use (default is `django.db.DEFAULT_DB_ALIAS`)
* `--yes` will proceed to deleting the record without asking for confirmation


Expand Down
6 changes: 3 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
version: "3.9"

# To build image and initialize container for subsequent tasks:
# docker-compose build
# docker-compose build --no-cache
# docker-compose run --rm buildenv /bin/bash
# curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/install-poetry.py | python -
# export PATH="/root/.local/bin:$PATH"
# poetry install
# poetry run python manage.py migrate

Expand All @@ -19,10 +17,12 @@ version: "3.9"
# To Publish (test pypi):
# docker-compose run --rm buildenv /bin/bash
# poetry config repositories.testpypi https://test.pypi.org/legacy/
# poetry config http-basic.testpypi vancly <password>
# poetry publish -r testpypi

# To Publish:
# docker-compose run --rm buildenv /bin/bash
# poetry config http-basic.pypi vancly <password>
# poetry publish

services:
Expand Down
2 changes: 0 additions & 2 deletions main/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path

urlpatterns = [
]
727 changes: 417 additions & 310 deletions poetry.lock

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "vmigration-helper"
version = "0.1.2"
version = "0.2.0"
homepage = "https://github.com/bluedenim/vmigration-helper"
description = "Van's Django Migration Helper"
authors = ["bluedenim <[email protected]>"]
Expand All @@ -12,8 +12,8 @@ exclude = ["main"]
keywords = ["django","migration","rollback"]

[tool.poetry.dependencies]
python = ">=3.6,<4.0"
Django = "^3.2.6"
python = ">=3.7,<4.0"
Django = "^1.11.17"

[tool.poetry.dev-dependencies]
twine = "^3.4.1"
Expand Down
3 changes: 0 additions & 3 deletions requirements.txt

This file was deleted.

51 changes: 51 additions & 0 deletions vmigration_helper/helpers/command.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from abc import ABC

from django.core.management import BaseCommand
from django.db import connections, DEFAULT_DB_ALIAS
from django.db.migrations.recorder import MigrationRecorder

from vmigration_helper.helpers.migration_records import MigrationRecordsHelper


class MigrationCommand(BaseCommand, ABC):
"""
Abstract base class for migration commands in this app.
This class provides a place for common concerns to all migration commands to be implemented. For instance, it
registers the "connection-name" optional parameter to the command and surfaces the parameter as
the "connection_name" property.
"""

def add_arguments(self, parser):
parser.add_argument(
'--connection-name',
default=DEFAULT_DB_ALIAS,
help=f'The connection to use for migration commands. Defaults to django.db.DEFAULT_DB_ALIAS'
)

def execute(self, *args, **options):
self.connection_name = options["connection_name"]
super().execute(*args, **options)

def create_migration_helper(self, connection=None) -> MigrationRecordsHelper:
"""
Initializes a connection and returns an instance of MigrationRecordsHelper initialized to the connection
provided.
:param connection: optional connection to use. If not provided, a connection to the current connection name
is created and used. If a connection is provided, its prepare_database() MUST have been called.
:returns: an instance of MigrationRecordsHelper
"""
if not connection:
connection = connections[self.connection_name]
connection.prepare_database()
return MigrationRecordsHelper(MigrationRecorder(connection))

@property
def connection_name(self) -> str:
return self._connection_name

@connection_name.setter
def connection_name(self, value: str) -> None:
self._connection_name = value
2 changes: 1 addition & 1 deletion vmigration_helper/helpers/migration_records.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import List, Optional

from django.db import DEFAULT_DB_ALIAS, connections
from django.db import connections, DEFAULT_DB_ALIAS
from django.db.migrations.recorder import MigrationRecorder
from django.db.models import QuerySet

Expand Down
17 changes: 6 additions & 11 deletions vmigration_helper/management/commands/migration_current_id.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,26 @@
from django.core.management import BaseCommand
from django.db import DEFAULT_DB_ALIAS, connections, OperationalError
from django.db.migrations.recorder import MigrationRecorder
from django.db import OperationalError
from django.db.models import Max

from vmigration_helper.helpers.migration_records import MigrationRecordsHelper
from vmigration_helper.helpers.command import MigrationCommand


class Command(BaseCommand):
class Command(MigrationCommand):
"""
Displays the ID of the last entry in the migration records (from the ``django_migrations`` table).
"""

@staticmethod
def create_snapshot_name(connection) -> int:
def create_snapshot_name(self) -> int:
"""
Returns the current max ID of the migration records table (django_migrations). If there are no records,
0 is returned.
"""
helper = MigrationRecordsHelper(MigrationRecorder(connection))
helper = self.create_migration_helper()
latest_migration_id = helper.get_migration_records_qs().aggregate(Max('id'))['id__max']
return latest_migration_id or 0

def handle(self, *args, **options):
try:
connection = connections[DEFAULT_DB_ALIAS]
connection.prepare_database()
print(self.create_snapshot_name(connection))
print(self.create_snapshot_name())
except OperationalError as e:
print(f'DB ERROR: {e}')
exit(1)
14 changes: 4 additions & 10 deletions vmigration_helper/management/commands/migration_delete.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
from django.core.management import BaseCommand
from django.db import DEFAULT_DB_ALIAS, connections
from django.db.migrations.recorder import MigrationRecorder
from vmigration_helper.helpers.command import MigrationCommand

from vmigration_helper.helpers.migration_records import MigrationRecordsHelper


class Command(BaseCommand):
class Command(MigrationCommand):
"""
Deletes a migration record from django_migrations. This operation is a low-level operation and
should be used only as a last resort when a migration cannot be rolled back, and deleting a record that is a
Expand All @@ -14,6 +10,7 @@ class Command(BaseCommand):
"""

def add_arguments(self, parser):
super().add_arguments(parser)
parser.add_argument(
'app',
help=(
Expand All @@ -38,11 +35,8 @@ def handle(self, *args, **options):
app = options['app']
name = options['name']
yes = options['yes']

if app and name:
connection = connections[DEFAULT_DB_ALIAS]
connection.prepare_database()
helper = MigrationRecordsHelper(MigrationRecorder(connection))
helper = self.create_migration_helper()

if not yes:
record = helper.get_migration_records_qs().filter(app=app, name=name)
Expand Down
13 changes: 5 additions & 8 deletions vmigration_helper/management/commands/migration_records.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
from django.core.management import BaseCommand
from django.db import DEFAULT_DB_ALIAS, connections, OperationalError
from django.db import OperationalError
from django.db.migrations.recorder import MigrationRecorder
from django.db.models import QuerySet

from vmigration_helper.helpers.migration_records import MigrationRecordsHelper
from vmigration_helper.helpers.command import MigrationCommand

FORMAT_CSV = 'csv'
FORMAT_CONSOLE = 'console'
DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%S%z'


class Command(BaseCommand):
class Command(MigrationCommand):
"""
Displays all the migration records (from the ``django_migrations`` table).
Expand Down Expand Up @@ -46,6 +45,7 @@ def _find_max_app_name_width(migration_query_set: QuerySet) -> int:
return 0

def add_arguments(self, parser):
super().add_arguments(parser)
parser.add_argument(
'--format',
default=FORMAT_CONSOLE,
Expand All @@ -54,11 +54,8 @@ def add_arguments(self, parser):

def handle(self, *args, **options):
print_format = options['format']

try:
connection = connections[DEFAULT_DB_ALIAS]
connection.prepare_database()
helper = MigrationRecordsHelper(MigrationRecorder(connection))
helper = self.create_migration_helper()
migrations_queryset = helper.get_migration_records_qs().all()

app_name_width = 0
Expand Down
12 changes: 5 additions & 7 deletions vmigration_helper/management/commands/migration_rollback.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import subprocess
from typing import List

from django.core.management import BaseCommand
from django.db import DEFAULT_DB_ALIAS, connections, OperationalError
from django.db import OperationalError
from django.db.migrations.recorder import MigrationRecorder

from vmigration_helper.helpers.migration_records import MigrationRecordsHelper
from vmigration_helper.helpers.command import MigrationCommand

MIGRATE_COMMAND = 'python manage.py migrate {app} {name}'


class Command(BaseCommand):
class Command(MigrationCommand):
"""
Rolls back migrations by unapplying entries in the migration records (django_migrations) whose IDs are greater
than the ID provided.
Expand All @@ -37,6 +36,7 @@ class Command(BaseCommand):
"""

def add_arguments(self, parser):
super().add_arguments(parser)
parser.add_argument(
'to_id',
type=int,
Expand Down Expand Up @@ -65,9 +65,7 @@ def handle(self, *args, **options):
migrate_cmd = options['migrate_cmd']

try:
connection = connections[DEFAULT_DB_ALIAS]
connection.prepare_database()
helper = MigrationRecordsHelper(MigrationRecorder(connection))
helper = self.create_migration_helper()
qs = helper.get_migration_records_qs().filter(id__gt=rollback_to_id).order_by('-id')
migration_records = list(qs) # type: List[MigrationRecorder.Migration]
squashed_migrations = helper.squash_migrations(migration_records)
Expand Down

0 comments on commit 2c2a058

Please sign in to comment.