11from __future__ import annotations
2+ from dataclasses import dataclass
23import datetime
34from itertools import chain
45import os
@@ -52,11 +53,22 @@ def _create_migrations_folder(migrations_path: str) -> bool:
5253 return True
5354
5455
55- async def _create_new_migration (app_config : AppConfig , auto = False ) -> None :
56+ @dataclass
57+ class NewMigrationMeta :
58+ migration_id : str
59+ migration_filename : str
60+ migration_path : str
61+
62+
63+ def _generate_migration_meta (app_config : AppConfig ) -> NewMigrationMeta :
5664 """
57- Creates a new migration file on disk .
65+ Generates the migration ID and filename .
5866 """
59- _id = datetime .datetime .now ().strftime ("%Y-%m-%dT%H:%M:%S" )
67+ # The microseconds originally weren't part of the ID, but there was a
68+ # chance that the IDs would clash if the migrations are generated
69+ # programatically in quick succession (e.g. in a unit test), so they had
70+ # to be added. The trade off is a longer ID.
71+ _id = datetime .datetime .now ().strftime ("%Y-%m-%dT%H:%M:%S:%f" )
6072
6173 # Originally we just used the _id as the filename, but colons aren't
6274 # supported in Windows, so we need to sanitize it. We don't want to
@@ -66,6 +78,23 @@ async def _create_new_migration(app_config: AppConfig, auto=False) -> None:
6678
6779 path = os .path .join (app_config .migrations_folder_path , f"{ filename } .py" )
6880
81+ return NewMigrationMeta (
82+ migration_id = _id , migration_filename = filename , migration_path = path
83+ )
84+
85+
86+ class NoChanges (Exception ):
87+ pass
88+
89+
90+ async def _create_new_migration (
91+ app_config : AppConfig , auto = False
92+ ) -> NewMigrationMeta :
93+ """
94+ Creates a new migration file on disk.
95+ """
96+ meta = _generate_migration_meta (app_config = app_config )
97+
6998 if auto :
7099 alter_statements = await AutoMigrationManager ().get_alter_statements (
71100 app_config = app_config
@@ -83,28 +112,31 @@ async def _create_new_migration(app_config: AppConfig, auto=False) -> None:
83112 )
84113
85114 if sum ([len (i .statements ) for i in alter_statements ]) == 0 :
86- print ("No changes detected - exiting." )
87- sys .exit (0 )
115+ raise NoChanges ()
88116
89117 file_contents = render_template (
90- migration_id = _id ,
118+ migration_id = meta . migration_id ,
91119 auto = True ,
92120 alter_statements = _alter_statements ,
93121 extra_imports = extra_imports ,
94122 extra_definitions = extra_definitions ,
95123 app_name = app_config .app_name ,
96124 )
97125 else :
98- file_contents = render_template (migration_id = _id , auto = False )
126+ file_contents = render_template (
127+ migration_id = meta .migration_id , auto = False
128+ )
99129
100130 # Beautify the file contents a bit.
101131 file_contents = black .format_str (
102132 file_contents , mode = black .FileMode (line_length = 82 )
103133 )
104134
105- with open (path , "w" ) as f :
135+ with open (meta . migration_path , "w" ) as f :
106136 f .write (file_contents )
107137
138+ return meta
139+
108140
109141###############################################################################
110142
@@ -117,7 +149,7 @@ async def get_alter_statements(
117149 Works out which alter statements are required.
118150 """
119151 migration_managers = await self .get_migration_managers (
120- app_name = app_config . app_name
152+ app_config = app_config
121153 )
122154
123155 schema_snapshot = SchemaSnapshot (migration_managers )
@@ -164,4 +196,8 @@ async def new(app_name: str, auto: bool = False):
164196 app_config = Finder ().get_app_config (app_name = app_name )
165197
166198 _create_migrations_folder (app_config .migrations_folder_path )
167- await _create_new_migration (app_config = app_config , auto = auto )
199+ try :
200+ await _create_new_migration (app_config = app_config , auto = auto )
201+ except NoChanges :
202+ print ("No changes detected - exiting." )
203+ sys .exit (0 )
0 commit comments