11from __future__ import annotations
22
33import os
4- import subprocess
54import sys
65import time
76from typing import TYPE_CHECKING , Any , cast
1413from plain .runtime import settings
1514from plain .utils .text import Truncator
1615
17- from . import migrations
18- from .backups .cli import cli as backups_cli
19- from .backups .core import DatabaseBackups
20- from .db import OperationalError
21- from .db import db_connection as _db_connection
22- from .migrations .autodetector import MigrationAutodetector
23- from .migrations .executor import MigrationExecutor
24- from .migrations .loader import AmbiguityError , MigrationLoader
25- from .migrations .migration import Migration , SettingsTuple
26- from .migrations .optimizer import MigrationOptimizer
27- from .migrations .questioner import (
16+ from .. import migrations
17+ from ..backups .core import DatabaseBackups
18+ from ..db import db_connection as _db_connection
19+ from ..migrations .autodetector import MigrationAutodetector
20+ from ..migrations .executor import MigrationExecutor
21+ from ..migrations .loader import AmbiguityError , MigrationLoader
22+ from ..migrations .migration import Migration , SettingsTuple
23+ from ..migrations .optimizer import MigrationOptimizer
24+ from ..migrations .questioner import (
2825 InteractiveMigrationQuestioner ,
2926 NonInteractiveMigrationQuestioner ,
3027)
31- from .migrations .recorder import MigrationRecorder
32- from .migrations .state import ModelState , ProjectState
33- from .migrations .writer import MigrationWriter
34- from .registry import models_registry
28+ from .. migrations .recorder import MigrationRecorder
29+ from .. migrations .state import ModelState , ProjectState
30+ from .. migrations .writer import MigrationWriter
31+ from .. registry import models_registry
3532
3633if TYPE_CHECKING :
37- from .backends .base .base import BaseDatabaseWrapper
38- from .migrations .operations .base import Operation
34+ from .. backends .base .base import BaseDatabaseWrapper
35+ from .. migrations .operations .base import Operation
3936
4037 db_connection = cast ("BaseDatabaseWrapper" , _db_connection )
4138else :
4239 db_connection = _db_connection
4340
4441
45- @register_cli ("models " )
42+ @register_cli ("migrations " )
4643@click .group ()
4744def cli () -> None :
48- """Database model management"""
49-
50-
51- cli .add_command (backups_cli )
52-
53-
54- @cli .command ()
55- @click .argument ("parameters" , nargs = - 1 )
56- def db_shell (parameters : tuple [str , ...]) -> None :
57- """Open an interactive database shell"""
58- try :
59- db_connection .client .runshell (list (parameters ))
60- except FileNotFoundError :
61- # Note that we're assuming the FileNotFoundError relates to the
62- # command missing. It could be raised for some other reason, in
63- # which case this error message would be inaccurate. Still, this
64- # message catches the common case.
65- click .secho (
66- f"You appear not to have the { db_connection .client .executable_name !r} program installed or on your path." ,
67- fg = "red" ,
68- err = True ,
69- )
70- sys .exit (1 )
71- except subprocess .CalledProcessError as e :
72- click .secho (
73- '"{}" returned non-zero exit status {}.' .format (
74- " " .join (e .cmd ),
75- e .returncode ,
76- ),
77- fg = "red" ,
78- err = True ,
79- )
80- sys .exit (e .returncode )
81-
82-
83- @cli .command ()
84- def db_wait () -> None :
85- """Wait for the database to be ready"""
86- attempts = 0
87- while True :
88- attempts += 1
89- waiting_for = False
90-
91- try :
92- db_connection .ensure_connection ()
93- except OperationalError :
94- waiting_for = True
95-
96- if waiting_for :
97- if attempts > 1 :
98- # After the first attempt, start printing them
99- click .secho (
100- f"Waiting for database (attempt { attempts } )" ,
101- fg = "yellow" ,
102- )
103- time .sleep (1.5 )
104- else :
105- click .secho ("✔ Database ready" , fg = "green" )
106- break
107-
108-
109- @cli .command (name = "list" )
110- @click .argument ("package_labels" , nargs = - 1 )
111- @click .option (
112- "--app-only" ,
113- is_flag = True ,
114- help = "Only show models from packages that start with 'app'." ,
115- )
116- def list_models (package_labels : tuple [str , ...], app_only : bool ) -> None :
117- """List all installed models"""
118-
119- packages = set (package_labels )
120-
121- for model in sorted (
122- models_registry .get_models (),
123- key = lambda m : (m .model_options .package_label , m .model_options .model_name ),
124- ):
125- pkg = model .model_options .package_label
126- pkg_name = packages_registry .get_package_config (pkg ).name
127- if app_only and not pkg_name .startswith ("app" ):
128- continue
129- if packages and pkg not in packages :
130- continue
131- fields = ", " .join (f .name for f in model ._model_meta .get_fields ())
132- click .echo (
133- f"{ click .style (pkg , fg = 'cyan' )} .{ click .style (model .__name__ , fg = 'blue' )} "
134- )
135- click .echo (f" table: { model .model_options .db_table } " )
136- click .echo (f" fields: { fields } " )
137- click .echo (f" package: { pkg_name } \n " )
45+ """Database migration management"""
13846
13947
14048@common_command
141- @register_cli ("makemigrations" , shortcut_for = "models " )
142- @cli .command ()
49+ @register_cli ("makemigrations" , shortcut_for = "migrations make " )
50+ @cli .command ("make" )
14351@click .argument ("package_labels" , nargs = - 1 )
14452@click .option (
14553 "--dry-run" ,
@@ -167,7 +75,7 @@ def list_models(package_labels: tuple[str, ...], app_only: bool) -> None:
16775 default = 1 ,
16876 help = "Verbosity level; 0=minimal output, 1=normal output, 2=verbose output, 3=very verbose output" ,
16977)
170- def makemigrations (
78+ def make (
17179 package_labels : tuple [str , ...],
17280 dry_run : bool ,
17381 empty : bool ,
@@ -344,8 +252,8 @@ def write_migration_files(
344252
345253
346254@common_command
347- @register_cli ("migrate" , shortcut_for = "models " )
348- @cli .command ()
255+ @register_cli ("migrate" , shortcut_for = "migrations apply " )
256+ @cli .command ("apply" )
349257@click .argument ("package_label" , required = False )
350258@click .argument ("migration_name" , required = False )
351259@click .option (
@@ -386,7 +294,7 @@ def write_migration_files(
386294 is_flag = True ,
387295 help = "Suppress migration output (used for test database creation)." ,
388296)
389- def migrate (
297+ def apply (
390298 package_label : str | None ,
391299 migration_name : str | None ,
392300 fake : bool ,
@@ -695,7 +603,7 @@ def describe_operation(operation: Any) -> tuple[str, bool]:
695603 )
696604
697605
698- @cli .command ()
606+ @cli .command ("list" )
699607@click .argument ("package_labels" , nargs = - 1 )
700608@click .option (
701609 "--format" ,
@@ -710,10 +618,10 @@ def describe_operation(operation: Any) -> tuple[str, bool]:
710618 default = 1 ,
711619 help = "Verbosity level; 0=minimal output, 1=normal output, 2=verbose output, 3=very verbose output" ,
712620)
713- def show_migrations (
621+ def list_migrations (
714622 package_labels : tuple [str , ...], format : str , verbosity : int
715623) -> None :
716- """Show all available migrations"""
624+ """Show all migrations"""
717625
718626 def _validate_package_names (package_names : tuple [str , ...]) -> None :
719627 has_bad_names = False
@@ -827,14 +735,14 @@ def print_deps(node: Any) -> str:
827735 show_list (db_connection , package_labels )
828736
829737
830- @cli .command ()
738+ @cli .command ("prune" )
831739@click .option (
832740 "--yes" ,
833741 is_flag = True ,
834742 help = "Skip confirmation prompt (for non-interactive use)." ,
835743)
836- def prune_migrations (yes : bool ) -> None :
837- """Prune stale migration records"""
744+ def prune (yes : bool ) -> None :
745+ """Remove stale migration records from the database """
838746 # Load migrations from disk and database
839747 loader = MigrationLoader (db_connection , ignore_no_migrations = True )
840748 recorder = MigrationRecorder (db_connection )
@@ -929,7 +837,7 @@ def prune_migrations(yes: bool) -> None:
929837 )
930838
931839
932- @cli .command ()
840+ @cli .command ("squash" )
933841@click .argument ("package_label" )
934842@click .argument ("start_migration_name" , required = False )
935843@click .argument ("migration_name" )
@@ -953,7 +861,7 @@ def prune_migrations(yes: bool) -> None:
953861 default = 1 ,
954862 help = "Verbosity level; 0=minimal output, 1=normal output, 2=verbose output, 3=very verbose output" ,
955863)
956- def squash_migrations (
864+ def squash (
957865 package_label : str ,
958866 start_migration_name : str | None ,
959867 migration_name : str ,
@@ -1016,7 +924,7 @@ def find_migration(
1016924 f"The migration '{ start_migration } ' cannot be found. Maybe it comes after "
1017925 f"the migration '{ migration } '?\n "
1018926 f"Have a look at:\n "
1019- f" plain models show- migrations { package_label } \n "
927+ f" plain migrations list { package_label } \n "
1020928 f"to debug this issue."
1021929 )
1022930
0 commit comments