#28120 closed Bug (fixed)
CharField: if max_length=True (or False): makemigrations generates a migration and migrate fails
| Reported by: | Carles Pina Estany | Owned by: | Carles Pina Estany |
|---|---|---|---|
| Component: | Database layer (models, ORM) | 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
If a model has a field like:
surname = models.CharField(max_length=True, null=True, blank=True)
python manage.py makemigrations writes a migration file but python manage.py migrate fails with an error:
carles@pinux:~/git/django-test/mysite$ python3 manage.py migrate
Operations to perform:
Apply all migrations: admin, app01, auth, contenttypes, sessions
Running migrations:
Applying app01.0002_auto_20170424_1440... OK
Applying app01.0003_auto_20170424_1446...Traceback (most recent call last):
File "/home/carles/git/django/django/db/backends/utils.py", line 60, in execute
return self.cursor.execute(sql)
File "/home/carles/git/django/django/db/backends/sqlite3/base.py", line 289, in execute
return Database.Cursor.execute(self, query)
sqlite3.OperationalError: near "True": syntax error
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "manage.py", line 15, in <module>
execute_from_command_line(sys.argv)
File "/home/carles/git/django/django/core/management/__init__.py", line 354, in execute_from_command_line
utility.execute()
File "/home/carles/git/django/django/core/management/__init__.py", line 348, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/home/carles/git/django/django/core/management/base.py", line 280, in run_from_argv
self.execute(*args, **cmd_options)
File "/home/carles/git/django/django/core/management/base.py", line 327, in execute
output = self.handle(*args, **options)
File "/home/carles/git/django/django/core/management/commands/migrate.py", line 200, in handle
fake_initial=fake_initial,
File "/home/carles/git/django/django/db/migrations/executor.py", line 113, in migrate
state = self._migrate_all_forwards(state, plan, full_plan, fake=fake, fake_initial=fake_initial)
File "/home/carles/git/django/django/db/migrations/executor.py", line 143, in _migrate_all_forwards
state = self.apply_migration(state, migration, fake=fake, fake_initial=fake_initial)
File "/home/carles/git/django/django/db/migrations/executor.py", line 240, in apply_migration
state = migration.apply(state, schema_editor)
File "/home/carles/git/django/django/db/migrations/migration.py", line 122, in apply
operation.database_forwards(self.app_label, schema_editor, old_state, project_state)
File "/home/carles/git/django/django/db/migrations/operations/fields.py", line 210, in database_forwards
schema_editor.alter_field(from_model, from_field, to_field)
File "/home/carles/git/django/django/db/backends/base/schema.py", line 490, in alter_field
old_db_params, new_db_params, strict)
File "/home/carles/git/django/django/db/backends/sqlite3/schema.py", line 258, in _alter_field
self._remake_table(model, alter_field=(old_field, new_field))
File "/home/carles/git/django/django/db/backends/sqlite3/schema.py", line 195, in _remake_table
self.create_model(temp_model)
File "/home/carles/git/django/django/db/backends/base/schema.py", line 290, in create_model
self.execute(sql, params or None)
File "/home/carles/git/django/django/db/backends/base/schema.py", line 109, in execute
cursor.execute(sql, params)
File "/home/carles/git/django/django/db/backends/utils.py", line 77, in execute
return super().execute(sql, params)
File "/home/carles/git/django/django/db/backends/utils.py", line 62, in execute
return self.cursor.execute(sql, params)
File "/home/carles/git/django/django/db/utils.py", line 90, in __exit__
raise dj_exc_value.with_traceback(traceback)
File "/home/carles/git/django/django/db/backends/utils.py", line 60, in execute
return self.cursor.execute(sql)
File "/home/carles/git/django/django/db/backends/sqlite3/base.py", line 289, in execute
return Database.Cursor.execute(self, query)
django.db.utils.OperationalError: near "True": syntax error
Change History (10)
comment:1 by , 9 years ago
| Owner: | changed from to |
|---|---|
| Status: | new → assigned |
comment:2 by , 9 years ago
| Triage Stage: | Unreviewed → Ready for checkin |
|---|---|
| Version: | 1.11 → master |
comment:6 by , 8 years ago
Дилян Палаузов this was required because issubclass(bool, int).
comment:7 by , 8 years ago
What about ensuring that max_length is an int, and not bool this way:
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -1045,8 +1045,7 @@ class CharField(Field):
id='fields.E120',
)
]
- elif (not isinstance(self.max_length, int) or isinstance(self.max_length, bool) or
- self.max_length <= 0):
+ elif type(self.max_length) != int or self.max_length <= 0:
return [
checks.Error(
"'max_length' must be a positive integer.",
There are more places in the code that check for not isinstance(..., int).
comment:8 by , 8 years ago
While uncommon that would prevent user defined int subclasses to work appropriately.
comment:9 by , 8 years ago
Why do you want to allow subclasses of int to be passed as max_length, but bool, which is a subclass of int, shall be excluded?
comment:10 by , 8 years ago
This is all getting into bike shedding territory but I assume that if a user is advanced enough to subclass int and pass it to max_length they know what they are doing.
On the opposite passing a max_length=bool is highly likely to be a beginner mistake. The fact bool subclasses int is really just a relic of the past which I assume most Python users are not aware of. In my case at least I wasn't until tackling this ticket.
PR