Opened 8 years ago
Last modified 7 years ago
#28431 closed Bug
Default value for BinaryField in reverse migration — at Version 1
| Reported by: | James | Owned by: | nobody |
|---|---|---|---|
| Component: | Core (System checks) | Version: | dev |
| Severity: | Normal | Keywords: | |
| Cc: | Triage Stage: | Ready for checkin | |
| Has patch: | yes | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description (last modified by )
Description
Initial migration has a default value '' for BinaryField.
Later, change default value to b'' and migrate.
Trying to undo this migration fails. It seems like '' is allowed during migration, but not in reverse migration.
Related issue
#22851 Default value for BinaryField
Reproduce
Python 3.6.0, Django 1.10.6, Postgres 9.5.4
- startproject djangoproject
- startapp firstapp
- firstapp/models.py:
class TableOne(models.Model):
field1 = models.BinaryField(default = '')
- makemigrations firstapp
- migrate firstapp 0001
- Modify firstapp/models.py
class TableOne(models.Model):
field1 = models.BinaryField(default = b'')
- migrate firstapp 0002
- migrate firstapp 0001
Error: TypeError: can't escape str to binary
Traceback (most recent call last):
File "manage.py", line 22, in <module>
execute_from_command_line(sys.argv)
File "C:\Py\py3_64\lib\site-packages\django\core\management\__init__.py", line 367, in execute_from_command_line
utility.execute()
File "C:\Py\py3_64\lib\site-packages\django\core\management\__init__.py", line 359, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "C:\Py\py3_64\lib\site-packages\django\core\management\base.py", line 294, in run_from_argv
self.execute(*args, **cmd_options)
File "C:\Py\py3_64\lib\site-packages\django\core\management\base.py", line 345, in execute
output = self.handle(*args, **options)
File "C:\Py\py3_64\lib\site-packages\django\core\management\commands\migrate.py", line 204, in handle
fake_initial=fake_initial,
File "C:\Py\py3_64\lib\site-packages\django\db\migrations\executor.py", line 119, in migrate
state = self._migrate_all_backwards(plan, full_plan, fake=fake)
File "C:\Py\py3_64\lib\site-packages\django\db\migrations\executor.py", line 194, in _migrate_all_backwards
self.unapply_migration(states[migration], migration, fake=fake)
File "C:\Py\py3_64\lib\site-packages\django\db\migrations\executor.py", line 264, in unapply_migration
state = migration.unapply(state, schema_editor)
File "C:\Py\py3_64\lib\site-packages\django\db\migrations\migration.py", line 178, in unapply
operation.database_backwards(self.app_label, schema_editor, from_state, to_state)
File "C:\Py\py3_64\lib\site-packages\django\db\migrations\operations\fields.py", line 210, in database_backwards
self.database_forwards(app_label, schema_editor, from_state, to_state)
File "C:\Py\py3_64\lib\site-packages\django\db\migrations\operations\fields.py", line 205, in database_forwards
schema_editor.alter_field(from_model, from_field, to_field)
File "C:\Py\py3_64\lib\site-packages\django\db\backends\base\schema.py", line 506, in alter_field
old_db_params, new_db_params, strict)
File "C:\Py\py3_64\lib\site-packages\django\db\backends\postgresql\schema.py", line 118, in _alter_field
new_db_params, strict,
File "C:\Py\py3_64\lib\site-packages\django\db\backends\base\schema.py", line 660, in _alter_field
params,
File "C:\Py\py3_64\lib\site-packages\django\db\backends\base\schema.py", line 112, in execute
cursor.execute(sql, params)
File "C:\Py\py3_64\lib\site-packages\django\db\backends\utils.py", line 80, in execute
return super(CursorDebugWrapper, self).execute(sql, params)
File "C:\Py\py3_64\lib\site-packages\django\db\backends\utils.py", line 65, in execute
return self.cursor.execute(sql, params)
TypeError: can't escape str to binary
Notes
site-packages\django\db\backends\base\shema.py def effective_default(self, field):
determines default as an empty <class 'str'>, when (default = '')
Possible Fix?
site-packages\django\db\backends\base\shema.py ~line 197
def effective_default(self, field):
if field.has_default():
default = field.get_default()
if field.get_internal_type() == "BinaryField" and not default:
default = six.binary_type()
elif not field.null and field.blank and field.empty_strings_allowed:
if field.get_internal_type() == "BinaryField":
default = six.binary_type()
else:
default = six.text_type()
elif getattr(field, 'auto_now', False)