44blocks into the forwards() and backwards() methods, in the right place.
55"""
66
7+ from __future__ import print_function
8+
79import sys
8- import datetime
910
1011from django .db .models .fields .related import RECURSIVE_RELATIONSHIP_CONSTANT
1112from django .db .models .fields import FieldDoesNotExist , NOT_PROVIDED , CharField , TextField
1213
13- from south import modelsinspector
14+ from south . modelsinspector import value_clean
1415from south .creator .freezer import remove_useless_attributes , model_key
16+ from south .utils import datetime_utils
17+ from south .utils .py3 import raw_input
18+
1519
1620class Action (object ):
1721 """
@@ -70,11 +74,11 @@ class AddModel(Action):
7074 db.create_table(%(table_name)r, (
7175 %(field_defs)s
7276 ))
73- db.send_create_signal(%(app_label)r, [%(model_name)r])'''
77+ db.send_create_signal(%(app_label)r, [%(model_name)r])''' [ 1 :] + " \n "
7478
7579 BACKWARDS_TEMPLATE = '''
7680 # Deleting model '%(model_name)s'
77- db.delete_table(%(table_name)r)'''
81+ db.delete_table(%(table_name)r)''' [ 1 :] + " \n "
7882
7983 def __init__ (self , model , model_def ):
8084 self .model = model
@@ -133,63 +137,65 @@ class _NullIssuesField(object):
133137 A field that might need to ask a question about rogue NULL values.
134138 """
135139
136- allow_third_null_option = False
140+ issue_with_backward_migration = False
137141 irreversible = False
138142
139143 IRREVERSIBLE_TEMPLATE = '''
140144 # User chose to not deal with backwards NULL issues for '%(model_name)s.%(field_name)s'
141- raise RuntimeError("Cannot reverse this migration. '%(model_name)s.%(field_name)s' and its values cannot be restored.")'''
145+ raise RuntimeError("Cannot reverse this migration. '%(model_name)s.%(field_name)s' and its values cannot be restored.")
146+
147+ # The following code is provided here to aid in writing a correct migration'''
142148
143149 def deal_with_not_null_no_default (self , field , field_def ):
144150 # If it's a CharField or TextField that's blank, skip this step.
145151 if isinstance (field , (CharField , TextField )) and field .blank :
146152 field_def [2 ]['default' ] = repr ("" )
147153 return
148154 # Oh dear. Ask them what to do.
149- print " ? The field '%s.%s' does not have a default specified, yet is NOT NULL." % (
155+ print ( " ? The field '%s.%s' does not have a default specified, yet is NOT NULL." % (
150156 self .model ._meta .object_name ,
151157 field .name ,
152- )
153- print " ? Since you are %s, you MUST specify a default" % self .null_reason
154- print " ? value to use for existing rows. Would you like to:"
155- print " ? 1. Quit now, and add a default to the field in models.py"
156- print " ? 2. Specify a one-off value to use for existing columns now"
157- if self .allow_third_null_option :
158- print " ? 3. Disable the backwards migration by raising an exception."
158+ ))
159+ print ( " ? Since you are %s, you MUST specify a default" % self .null_reason )
160+ print ( " ? value to use for existing rows. Would you like to:" )
161+ print ( " ? 1. Quit now" + ( "." if self . issue_with_backward_migration else " , and add a default to the field in models.py" ))
162+ print ( " ? 2. Specify a one-off value to use for existing columns now" )
163+ if self .issue_with_backward_migration :
164+ print ( " ? 3. Disable the backwards migration by raising an exception; you can edit the migration to fix it later" )
159165 while True :
160166 choice = raw_input (" ? Please select a choice: " )
161167 if choice == "1" :
162168 sys .exit (1 )
163169 elif choice == "2" :
164170 break
165- elif choice == "3" and self .allow_third_null_option :
171+ elif choice == "3" and self .issue_with_backward_migration :
166172 break
167173 else :
168- print " ! Invalid choice."
174+ print ( " ! Invalid choice." )
169175 if choice == "2" :
170176 self .add_one_time_default (field , field_def )
171177 elif choice == "3" :
172178 self .irreversible = True
173179
174180 def add_one_time_default (self , field , field_def ):
175181 # OK, they want to pick their own one-time default. Who are we to refuse?
176- print " ? Please enter Python code for your one-off default value."
177- print " ? The datetime module is available, so you can do e.g. datetime.date.today()"
182+ print ( " ? Please enter Python code for your one-off default value." )
183+ print ( " ? The datetime module is available, so you can do e.g. datetime.date.today()" )
178184 while True :
179185 code = raw_input (" >>> " )
180186 if not code :
181- print " ! Please enter some code, or 'exit' (with no quotes) to exit."
187+ print ( " ! Please enter some code, or 'exit' (with no quotes) to exit." )
182188 elif code == "exit" :
183189 sys .exit (1 )
184190 else :
185191 try :
186- result = eval (code , {}, {"datetime" : datetime })
187- except (SyntaxError , NameError ), e :
188- print " ! Invalid input: %s" % e
192+ result = eval (code , {}, {"datetime" : datetime_utils })
193+ except (SyntaxError , NameError ) as e :
194+ print ( " ! Invalid input: %s" % e )
189195 else :
190196 break
191197 # Right, add the default in.
192- field_def [2 ]['default' ] = repr (result )
198+ field_def [2 ]['default' ] = value_clean (result )
193199
194200 def irreversable_code (self , field ):
195201 return self .IRREVERSIBLE_TEMPLATE % {
@@ -209,11 +215,13 @@ class AddField(Action, _NullIssuesField):
209215
210216 FORWARDS_TEMPLATE = '''
211217 # Adding field '%(model_name)s.%(field_name)s'
212- db.add_column(%(table_name)r, %(field_name)r, %(field_def)s, keep_default=False)'''
218+ db.add_column(%(table_name)r, %(field_name)r,
219+ %(field_def)s,
220+ keep_default=False)''' [1 :] + "\n "
213221
214222 BACKWARDS_TEMPLATE = '''
215223 # Deleting field '%(model_name)s.%(field_name)s'
216- db.delete_column(%(table_name)r, %(field_column)r)'''
224+ db.delete_column(%(table_name)r, %(field_column)r)''' [ 1 :] + " \n "
217225
218226 def __init__ (self , model , field , field_def ):
219227 self .model = model
@@ -260,7 +268,7 @@ class DeleteField(AddField):
260268 """
261269
262270 null_reason = "removing this field"
263- allow_third_null_option = True
271+ issue_with_backward_migration = True
264272
265273 def console_line (self ):
266274 "Returns the string to print on the console, e.g. ' + Added field foo'"
@@ -277,7 +285,7 @@ def backwards_code(self):
277285 if not self .irreversible :
278286 return AddField .forwards_code (self )
279287 else :
280- return self .irreversable_code (self .field )
288+ return self .irreversable_code (self .field ) + AddField . forwards_code ( self )
281289
282290
283291class ChangeField (Action , _NullIssuesField ):
@@ -309,7 +317,7 @@ def __init__(self, model, old_field, new_field, old_def, new_def):
309317 self .deal_with_not_null_no_default (self .new_field , self .new_def )
310318 if not self .old_field .null and self .new_field .null and not old_default :
311319 self .null_reason = "making this field nullable"
312- self .allow_third_null_option = True
320+ self .issue_with_backward_migration = True
313321 self .deal_with_not_null_no_default (self .old_field , self .old_def )
314322
315323 def console_line (self ):
@@ -347,10 +355,11 @@ def forwards_code(self):
347355 return self ._code (self .old_field , self .new_field , self .new_def )
348356
349357 def backwards_code (self ):
358+ change_code = self ._code (self .new_field , self .old_field , self .old_def )
350359 if not self .irreversible :
351- return self . _code ( self . new_field , self . old_field , self . old_def )
360+ return change_code
352361 else :
353- return self .irreversable_code (self .old_field )
362+ return self .irreversable_code (self .old_field ) + change_code
354363
355364
356365class AddUnique (Action ):
@@ -360,11 +369,11 @@ class AddUnique(Action):
360369
361370 FORWARDS_TEMPLATE = '''
362371 # Adding unique constraint on '%(model_name)s', fields %(field_names)s
363- db.create_unique(%(table_name)r, %(fields)r)'''
372+ db.create_unique(%(table_name)r, %(fields)r)''' [ 1 :] + " \n "
364373
365374 BACKWARDS_TEMPLATE = '''
366375 # Removing unique constraint on '%(model_name)s', fields %(field_names)s
367- db.delete_unique(%(table_name)r, %(fields)r)'''
376+ db.delete_unique(%(table_name)r, %(fields)r)''' [ 1 :] + " \n "
368377
369378 prepend_backwards = True
370379
@@ -428,11 +437,11 @@ class AddIndex(AddUnique):
428437
429438 FORWARDS_TEMPLATE = '''
430439 # Adding index on '%(model_name)s', fields %(field_names)s
431- db.create_index(%(table_name)r, %(fields)r)'''
440+ db.create_index(%(table_name)r, %(fields)r)''' [ 1 :] + " \n "
432441
433442 BACKWARDS_TEMPLATE = '''
434443 # Removing index on '%(model_name)s', fields %(field_names)s
435- db.delete_index(%(table_name)r, %(fields)r)'''
444+ db.delete_index(%(table_name)r, %(fields)r)''' [ 1 :] + " \n "
436445
437446 def console_line (self ):
438447 "Returns the string to print on the console, e.g. ' + Added field foo'"
@@ -470,16 +479,17 @@ class AddM2M(Action):
470479
471480 FORWARDS_TEMPLATE = '''
472481 # Adding M2M table for field %(field_name)s on '%(model_name)s'
473- db.create_table(%(table_name)r, (
482+ m2m_table_name = %(table_name)s
483+ db.create_table(m2m_table_name, (
474484 ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
475485 (%(left_field)r, models.ForeignKey(orm[%(left_model_key)r], null=False)),
476486 (%(right_field)r, models.ForeignKey(orm[%(right_model_key)r], null=False))
477487 ))
478- db.create_unique(%(table_name)r , [%(left_column)r, %(right_column)r])'''
488+ db.create_unique(m2m_table_name , [%(left_column)r, %(right_column)r])''' [ 1 :] + " \n "
479489
480490 BACKWARDS_TEMPLATE = '''
481491 # Removing M2M table for field %(field_name)s on '%(model_name)s'
482- db.delete_table(' %(table_name)s' )'''
492+ db.delete_table(%(table_name)s)''' [ 1 :] + " \n "
483493
484494 def __init__ (self , model , field ):
485495 self .model = model
@@ -492,13 +502,25 @@ def console_line(self):
492502 self .model ._meta .app_label ,
493503 self .model ._meta .object_name ,
494504 )
495-
505+
506+ def table_name (self ):
507+ # This is part of a workaround for the fact that Django uses
508+ # different shortening for automatically generated m2m table names
509+ # (as opposed to any explicitly specified table name)
510+ f = self .field
511+ explicit = f .db_table
512+ if explicit :
513+ return "%r" % explicit
514+ else :
515+ auto = "%s_%s" % (self .model ._meta .db_table , f .name )
516+ return 'db.shorten_name(%r)' % auto
517+
496518 def forwards_code (self ):
497519
498520 return self .FORWARDS_TEMPLATE % {
499521 "model_name" : self .model ._meta .object_name ,
500522 "field_name" : self .field .name ,
501- "table_name" : self .field . m2m_db_table (),
523+ "table_name" : self .table_name (),
502524 "left_field" : self .field .m2m_column_name ()[:- 3 ], # Remove the _id part
503525 "left_column" : self .field .m2m_column_name (),
504526 "left_model_key" : model_key (self .model ),
@@ -512,7 +534,7 @@ def backwards_code(self):
512534 return self .BACKWARDS_TEMPLATE % {
513535 "model_name" : self .model ._meta .object_name ,
514536 "field_name" : self .field .name ,
515- "table_name" : self .field . m2m_db_table (),
537+ "table_name" : self .table_name (),
516538 }
517539
518540
0 commit comments