#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 , 8 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:2 by , 8 years ago
Triage Stage: | Unreviewed → Ready for checkin |
---|---|
Version: | 1.11 → master |
comment:6 by , 7 years ago
Дилян Палаузов this was required because issubclass(bool, int)
.
comment:7 by , 7 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 , 7 years ago
While uncommon that would prevent user defined int
subclasses to work appropriately.
comment:9 by , 7 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 , 7 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