Skip to content

Commit 4f86cfe

Browse files
committed
added basic tests for piccolo migrations forwards / backwards entrypoints
1 parent 6e51d87 commit 4f86cfe

File tree

8 files changed

+355
-16
lines changed

8 files changed

+355
-16
lines changed

piccolo.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/bin/bash
2+
# This is used for running Piccolo commands on the example project within
3+
# tests.
4+
export PICCOLO_CONF="tests.postgres_conf"
5+
python -m piccolo.main $@

piccolo/apps/migrations/commands/backwards.py

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@
55

66

77
class BackwardsMigrationManager(BaseMigrationManager):
8-
def __init__(self, app_name: str, migration_id: str):
8+
def __init__(
9+
self, app_name: str, migration_id: str, auto_agree: bool = False
10+
):
911
self.migration_id = migration_id
1012
self.app_name = app_name
13+
self.auto_agree = auto_agree
1114
super().__init__()
1215

1316
def run(self):
@@ -57,10 +60,14 @@ def run(self):
5760

5861
#######################################################################
5962

60-
_continue = input(
61-
"About to undo the following migrations:\n"
62-
f"{reversed_migration_ids}\n"
63-
"Enter y to continue.\n"
63+
_continue = (
64+
input(
65+
"About to undo the following migrations:\n"
66+
f"{reversed_migration_ids}\n"
67+
"Enter y to continue.\n"
68+
)
69+
if not self.auto_agree
70+
else "y"
6471
)
6572
if _continue == "y":
6673
print("Undoing migrations")
@@ -80,7 +87,9 @@ def run(self):
8087
print("Not proceeding.")
8188

8289

83-
def backwards(app_name: str, migration_id: str = "1"):
90+
def backwards(
91+
app_name: str, migration_id: str = "1", auto_agree: bool = False
92+
):
8493
"""
8594
Undo migrations up to a specific migration.
8695
@@ -91,16 +100,23 @@ def backwards(app_name: str, migration_id: str = "1"):
91100
Migrations will be reversed up to and including this migration_id.
92101
Specify a value of 'all' to undo all of the migrations. Specify a
93102
value of '1' to undo the most recent migration.
103+
:param auto_agree:
104+
Automatically agree to any input prompts.
105+
94106
"""
95107
if app_name == "all":
96108
sorted_app_names = BaseMigrationManager().get_sorted_app_names()
97109
sorted_app_names.reverse()
98110

99-
_continue = input(
100-
"You're about to undo the migrations for the following apps:\n"
101-
f"{sorted_app_names}\n"
102-
"Are you sure you want to continue?\n"
103-
"Enter y to continue.\n"
111+
_continue = (
112+
input(
113+
"You're about to undo the migrations for the following apps:\n"
114+
f"{sorted_app_names}\n"
115+
"Are you sure you want to continue?\n"
116+
"Enter y to continue.\n"
117+
)
118+
if not auto_agree
119+
else "y"
104120
)
105121
if _continue == "y":
106122
for _app_name in sorted_app_names:
@@ -111,6 +127,6 @@ def backwards(app_name: str, migration_id: str = "1"):
111127
manager.run()
112128
else:
113129
manager = BackwardsMigrationManager(
114-
app_name=app_name, migration_id=migration_id
130+
app_name=app_name, migration_id=migration_id, auto_agree=auto_agree
115131
)
116132
manager.run()

piccolo/query/methods/alter.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,12 +229,20 @@ def querystring(self) -> QueryString:
229229
class DropTable:
230230
tablename: str
231231
cascade: bool
232+
if_exists: bool
232233

233234
@property
234235
def querystring(self) -> QueryString:
235-
query = f'DROP TABLE "{self.tablename}"'
236+
query = "DROP TABLE"
237+
238+
if self.if_exists:
239+
query += " IF EXISTS"
240+
241+
query += f" {self.tablename}"
242+
236243
if self.cascade:
237244
query += " CASCADE"
245+
238246
return QueryString(query)
239247

240248

@@ -294,12 +302,16 @@ def drop_default(self, column: t.Union[str, Column]) -> Alter:
294302
self._drop_default.append(DropDefault(column=column))
295303
return self
296304

297-
def drop_table(self, cascade: bool = False) -> Alter:
305+
def drop_table(
306+
self, cascade: bool = False, if_exists: bool = False
307+
) -> Alter:
298308
"""
299309
Band.alter().drop_table()
300310
"""
301311
self._drop_table = DropTable(
302-
tablename=self.table._meta.tablename, cascade=cascade
312+
tablename=self.table._meta.tablename,
313+
cascade=cascade,
314+
if_exists=if_exists,
303315
)
304316
return self
305317

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from __future__ import annotations
2+
from unittest import TestCase
3+
import typing as t
4+
5+
from piccolo.apps.migrations.commands.backwards import backwards
6+
from piccolo.apps.migrations.commands.forwards import forwards
7+
8+
from tests.example_app.tables import (
9+
Manager,
10+
Band,
11+
Venue,
12+
Concert,
13+
Ticket,
14+
Poster,
15+
)
16+
17+
if t.TYPE_CHECKING:
18+
from piccolo.table import Table
19+
20+
21+
TABLE_CLASSES: t.List[t.Type[Table]] = [
22+
Manager,
23+
Band,
24+
Venue,
25+
Concert,
26+
Ticket,
27+
Poster,
28+
]
29+
30+
31+
class TestForwardsBackwards(TestCase):
32+
"""
33+
How to test this? Add migrations for the example app, and just run them
34+
forwards and backwards. Or create a new app with loads of migrations in
35+
it.
36+
"""
37+
38+
# TODO - use pytest parameterise ...
39+
def test_forwards_backwards_all_migrations(self):
40+
"""
41+
Test running all of the migrations forwards, then backwards.
42+
"""
43+
forwards(app_name="example_app", migration_id="all")
44+
45+
# Check the tables exist
46+
for table_class in TABLE_CLASSES:
47+
self.assertTrue(table_class.table_exists().run_sync())
48+
49+
backwards(app_name="example_app", migration_id="all", auto_agree=True)
50+
51+
# Check the tables don't exist
52+
for table_class in TABLE_CLASSES:
53+
self.assertTrue(not table_class.table_exists().run_sync())
54+
55+
def tearDown(self):
56+
for table_class in TABLE_CLASSES:
57+
table_class.alter().drop_table(if_exists=True).run_sync()

tests/example_app/piccolo_app.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1+
import os
2+
13
from piccolo.conf.apps import AppConfig
24

35
from .tables import Manager, Band, Venue, Concert, Ticket, Poster
46

57

8+
CURRENT_DIRECTORY = os.path.dirname(os.path.abspath(__file__))
9+
10+
611
APP_CONFIG = AppConfig(
712
app_name="example_app",
813
table_classes=[Manager, Band, Venue, Concert, Ticket, Poster],
9-
migrations_folder_path="",
14+
migrations_folder_path=os.path.join(
15+
CURRENT_DIRECTORY, "piccolo_migrations"
16+
),
1017
commands=[],
1118
)
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
from piccolo.apps.migrations.auto import MigrationManager
2+
from piccolo.columns.base import OnDelete
3+
from piccolo.columns.base import OnUpdate
4+
from piccolo.table import Table
5+
6+
7+
class Manager(Table, tablename="manager"):
8+
pass
9+
10+
11+
ID = "2020-12-17T18:44:30"
12+
VERSION = "0.14.7"
13+
14+
15+
async def forwards():
16+
manager = MigrationManager(migration_id=ID, app_name="example_app")
17+
18+
manager.add_table("Manager", tablename="manager")
19+
manager.add_table("Band", tablename="band")
20+
21+
manager.add_column(
22+
table_class_name="Band",
23+
tablename="band",
24+
column_name="name",
25+
column_class_name="Varchar",
26+
params={
27+
"length": 50,
28+
"default": "",
29+
"null": False,
30+
"primary": False,
31+
"key": False,
32+
"unique": False,
33+
"index": False,
34+
},
35+
)
36+
37+
manager.add_column(
38+
table_class_name="Band",
39+
tablename="band",
40+
column_name="manager",
41+
column_class_name="ForeignKey",
42+
params={
43+
"references": Manager,
44+
"on_delete": OnDelete.cascade,
45+
"on_update": OnUpdate.cascade,
46+
"default": None,
47+
"null": True,
48+
"primary": False,
49+
"key": False,
50+
"unique": False,
51+
"index": False,
52+
},
53+
)
54+
55+
manager.add_column(
56+
table_class_name="Band",
57+
tablename="band",
58+
column_name="popularity",
59+
column_class_name="Integer",
60+
params={
61+
"default": 0,
62+
"null": False,
63+
"primary": False,
64+
"key": False,
65+
"unique": False,
66+
"index": False,
67+
},
68+
)
69+
70+
manager.add_column(
71+
table_class_name="Manager",
72+
tablename="manager",
73+
column_name="name",
74+
column_class_name="Varchar",
75+
params={
76+
"length": 50,
77+
"default": "",
78+
"null": False,
79+
"primary": False,
80+
"key": False,
81+
"unique": False,
82+
"index": False,
83+
},
84+
)
85+
86+
return manager

0 commit comments

Comments
 (0)