44import logging
55from functools import wraps
66import argparse
7+ import re
78from pathlib import Path
89from typing import Optional , List , Dict , Any , Union , Callable
910
@@ -85,57 +86,64 @@ def list_templates() -> None:
8586
8687
8788def _auto_configure_migrations (directory : str , db_url : Optional [str ] = None , model_path : Optional [str ] = None ) -> None :
88- """自动配置 alembic.ini 和 env.py 文件
89+ """Automatically configure alembic.ini and env.py
8990
9091 Args:
91- directory: 迁移文件目录
92- db_url: 数据库 URL
93- model_path: 模型文件路径
92+ directory: Directory where migration files are stored
93+ db_url: Database URL
94+ model_path: Path to the model file
9495 """
9596 # Configure alembic.ini
9697 if db_url :
9798 alembic_ini_path = os .path .join (directory , 'alembic.ini' )
9899 if os .path .exists (alembic_ini_path ):
100+
99101 with open (alembic_ini_path , 'r' ) as f :
100102 content = f .read ()
101103
102- # Replace the database URL
103- content = content .replace ('sqlalchemy.url = driver://user:pass@localhost/dbname' ,
104- f'sqlalchemy.url = { db_url } ' )
104+ # Replace the database URL using regex pattern for more flexibility
105+ pattern = r'sqlalchemy\.url\s*=\s*[^\n]+'
106+ replacement = f'sqlalchemy.url = { db_url } '
107+ new_content = re .sub (pattern , replacement , content )
105108
106- with open (alembic_ini_path , 'w' ) as f :
107- f .write (content )
108- print (f"Successfully configured the database URL: { db_url } " )
109+ if new_content != content :
110+ with open (alembic_ini_path , 'w' ) as f :
111+ f .write (new_content )
112+ print (f"Successfully configured the database URL: { db_url } " )
113+ else :
114+ print ("Warning: Could not find database URL configuration in alembic.ini" )
109115
110116 # Configure env.py
111117 if model_path :
112118 env_py_path = os .path .join (directory , 'env.py' )
113119 if os .path .exists (env_py_path ):
120+
114121 with open (env_py_path , 'r' ) as f :
115122 content = f .read ()
116123
117124 try :
118125 module_path , class_name = model_path .rsplit ('.' , 1 )
119-
120- # Replace the import statement and target_metadata setting
121126 # Allow importing from the parent directory
122127 import_statement = f"sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))\n from { module_path } import { class_name } \n target_metadata = { class_name } .metadata"
123128
124- # Replace the import statement
125- content = content . replace (
126- "# add your model's MetaData object here\n # for 'autogenerate' support\n # from myapp import mymodel \n # target_metadata = mymodel.Base.metadata" ,
127- f"# add your model's MetaData object here \n # for 'autogenerate' support \n { import_statement } "
128- )
129+ # Replace the import statement using regex for more robustness
130+ import_pattern = r"# add your model's MetaData object here\s*\n# for 'autogenerate' support\s*\n# from myapp import mymodel\s*\n# target_metadata = mymodel\.Base\.metadata"
131+ import_replacement = f "# add your model's MetaData object here\n # for 'autogenerate' support\n { import_statement } "
132+
133+ new_content = re . sub ( import_pattern , import_replacement , content )
129134
130135 # Replace the target_metadata setting
131- content = content .replace (
132- "target_metadata = config.attributes.get('sqlalchemy.metadata', None)" ,
133- f"# target_metadata = config.attributes.get('sqlalchemy.metadata', None)\n # Already set by the import above"
134- )
135-
136- with open (env_py_path , 'w' ) as f :
137- f .write (content )
138- print (f"Successfully configured the model path: { model_path } " )
136+ metadata_pattern = r"target_metadata = config\.attributes\.get\('sqlalchemy\.metadata', None\)"
137+ metadata_replacement = f"# target_metadata = config.attributes.get('sqlalchemy.metadata', None)\n # Already set by the import above"
138+
139+ new_content = re .sub (metadata_pattern , metadata_replacement , new_content )
140+
141+ if new_content != content :
142+ with open (env_py_path , 'w' ) as f :
143+ f .write (new_content )
144+ print (f"Successfully configured the model path: { model_path } " )
145+ else :
146+ print ("Warning: Could not find expected patterns in env.py, please manually configure it" )
139147 except ValueError :
140148 print (f"Warning: Could not parse the model path { model_path } , please manually configure env.py" )
141149
@@ -148,15 +156,21 @@ def _special_configure_for_sqlite(directory: str, model_path: Optional[str] = No
148156 with open (env_py_path , 'r' ) as f :
149157 content = f .read ()
150158 try :
151- content = content .replace (
152- " context.configure(\n connection=connection,\n target_metadata=target_metadata,\n process_revision_directives=process_revision_directives,\n )" ,
153- " from sqlalchemy.engine import Connection\n def is_sqlite(conn: Connection) -> bool:\n return conn.dialect.name == \" sqlite\" \n context.configure(\n connection=connection,\n target_metadata=target_metadata,\n process_revision_directives=process_revision_directives,\n render_as_batch=is_sqlite(connection),\n )"
154- )
155- with open (env_py_path , 'w' ) as f :
156- f .write (content )
157- except ValueError :
159+ # Use regex pattern to match the context.configure block more flexibly
160+ pattern = r'\s+context\.configure\(\s*\n\s+connection=connection,\s*\n\s+target_metadata=target_metadata,\s*\n\s+process_revision_directives=process_revision_directives,\s*\n\s+\)'
161+ replacement = "\n from sqlalchemy.engine import Connection\n def is_sqlite(conn: Connection) -> bool:\n return conn.dialect.name == \" sqlite\" \n context.configure(\n connection=connection,\n target_metadata=target_metadata,\n process_revision_directives=process_revision_directives,\n render_as_batch=is_sqlite(connection),\n )"
162+
163+ new_content = re .sub (pattern , replacement , content )
164+
165+ if new_content != content :
166+ with open (env_py_path , 'w' ) as f :
167+ f .write (new_content )
168+ else :
169+ print (
170+ "Warning: Could not find context.configure block in env.py, please manually add render_as_batch=True for SQLite support" )
171+ except Exception as e :
158172 print (
159- "Warning: If your database is SQLite, you need to manually add `render_as_batch=True` in run_migrations_online() to avoid migration errors caused by SQLite's limited support for ALTER TABLE." )
173+ f "Warning: Could not configure SQLite support: { str ( e ) } . If your database is SQLite, you need to manually add `render_as_batch=True` in run_migrations_online() to avoid migration errors caused by SQLite's limited support for ALTER TABLE." )
160174 else :
161175 print (
162176 "Warning: If your database is SQLite, you need to manually add `render_as_batch=True` in run_migrations_online() to avoid migration errors caused by SQLite's limited support for ALTER TABLE." )
@@ -193,8 +207,8 @@ def init(directory: str = 'migrations', multidb: bool = False, template: Optiona
193207 print (
194208 "Cannot find models module.\n Please provide your database URL with \" --db-url=<YOUR_DB_URL>\" ." )
195209 return
196- except Exception as e :
197- print ("Please provide your database URL with \" --db-url=<YOUR_DB_URL>\" ." )
210+ except ImportError :
211+ print ("Cannot find models module. \n Please provide your database URL with \" --db-url=<YOUR_DB_URL>\" ." )
198212 return
199213
200214 if not model_path :
@@ -214,15 +228,16 @@ def init(directory: str = 'migrations', multidb: bool = False, template: Optiona
214228 print (
215229 "Cannot find models module.\n Please provide your model path with \" --model-path=<YOUR_MODEL_PATH>\" ." )
216230 return
217- except Exception as e :
218- print ("Please provide your model path with \" --model-path=<YOUR_MODEL_PATH>\" ." )
231+ except ImportError :
232+ print ("Cannot find models module. \n Please provide your model path with \" --model-path=<YOUR_MODEL_PATH>\" ." )
219233 return
220234
221235 # Ensure the directory exists
222236 os .makedirs (directory , exist_ok = True )
223237
224238 config = Config (directory )
225239 template_path = config ._get_template_path (template ) if template is not None else config ._get_template_path ()
240+ print (template_path )
226241 command .init (config , directory , template = template_path , package = package )
227242
228243 _auto_configure_migrations (directory , db_url , model_path )
0 commit comments