Opened 3 years ago
Closed 3 years ago
#34820 closed Bug (fixed)
Migrations crashes when changing ForeignObject properties.
| Reported by: | puc_dong | Owned by: | puc_dong |
|---|---|---|---|
| Component: | Database layer (models, ORM) | Version: | 4.2 |
| 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 )
Execute migrate to report an error
If the models.ForeignObject attribute is used, changing null or blank to generate a new migration record will result in an error message when executing the migrate again
File "/Users/donghao/test/test_migations/manage.py", line 22, in <module>
execute_from_command_line(sys.argv)
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line
utility.execute()
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/core/management/__init__.py", line 436, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/core/management/base.py", line 412, in run_from_argv
self.execute(*args, **cmd_options)
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/core/management/base.py", line 458, in execute
output = self.handle(*args, **options)
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/core/management/base.py", line 106, in wrapper
res = handle_func(*args, **kwargs)
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/core/management/commands/migrate.py", line 356, in handle
post_migrate_state = executor.migrate(
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/db/migrations/executor.py", line 135, in migrate
state = self._migrate_all_forwards(
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/db/migrations/executor.py", line 167, in _migrate_all_forwards
state = self.apply_migration(
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/db/migrations/executor.py", line 252, in apply_migration
state = migration.apply(state, schema_editor)
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/db/migrations/migration.py", line 132, in apply
operation.database_forwards(
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/db/migrations/operations/fields.py", line 235, in database_forwards
schema_editor.alter_field(from_model, from_field, to_field)
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/db/backends/base/schema.py", line 785, in alter_field
if not self._field_should_be_altered(old_field, new_field):
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/db/backends/base/schema.py", line 1530, in _field_should_be_altered
return self.quote_name(old_field.column) != self.quote_name(
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/db/backends/base/schema.py", line 204, in quote_name
return self.connection.ops.quote_name(name)
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/db/backends/mysql/operations.py", line 184, in quote_name
if name.startswith("`") and name.endswith("`"):
AttributeError: 'NoneType' object has no attribute 'startswith'
models:
# app1:
class Model1(models.Model):
name = models.CharField(max_length=50)
# app2:
class Model2(models.Model):
name = models.CharField(max_length=100, db_index=True)
clu_id = models.BigIntegerField()
scenes = models.ForeignObject(Model1,
from_fields=['clu_id'],
to_fields=['id'],
null=True,
related_name='model2_config',
on_delete=models.DO_NOTHING)
Successfully executed makemigrations&migrate for the first time. When I add blank=True to the scenes field of Model2, and then execute makemigrations and migrate to report an error
All migration files:
app1.0001_initial.py
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Model1',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50)),
],
),
]
app2.0001_initial.py
class Migration(migrations.Migration):
initial = True
dependencies = [
('app_1', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Model2',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(db_index=True, max_length=100)),
('clu_id', models.BigIntegerField()),
('scenes', models.ForeignObject(from_fields=['clu_id'], null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='model2_config', to='app_1.model1', to_fields=['id'])),
],
),
]
app2.0002_alter_model2_scenes.py
class Migration(migrations.Migration):
dependencies = [
('app_1', '0001_initial'),
('app_2', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='model2',
name='scenes',
field=models.ForeignObject(blank=True, from_fields=('clu_id',), null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='model2_config', to='app_1.model1', to_fields=('id',)),
),
]
Change History (17)
comment:1 by , 3 years ago
| Description: | modified (diff) |
|---|
comment:2 by , 3 years ago
| Description: | modified (diff) |
|---|
follow-up: 6 comment:3 by , 3 years ago
| Component: | Database layer (models, ORM) → Migrations |
|---|---|
| Needs tests: | set |
| Resolution: | → needsinfo |
| Status: | new → closed |
comment:4 by , 3 years ago
| Component: | Migrations → Database layer (models, ORM) |
|---|---|
| Description: | modified (diff) |
| Needs tests: | unset |
comment:5 by , 3 years ago
| Resolution: | needsinfo |
|---|---|
| Status: | closed → new |
comment:6 by , 3 years ago
Replying to Mariusz Felisiak:
I'm not sure how to you reach a field without a
columnin migrations. I don't think you've explained the issue in enough detail to confirm a bug in Django, can you provide a sample project or test case that reproduces the issue?
I submitted my case code and process
comment:7 by , 3 years ago
(simpleui) ☁ test_migations python manage.py makemigrations
System check identified some issues:
WARNINGS:
app_1.Model1: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'.
HINT: Configure the DEFAULT_AUTO_FIELD setting or the App1Config.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'.
app_2.Model2: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'.
HINT: Configure the DEFAULT_AUTO_FIELD setting or the App2Config.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'.
Migrations for 'app_1':
app_1/migrations/0001_initial.py
- Create model Model1
Migrations for 'app_2':
app_2/migrations/0001_initial.py
- Create model Model2
(simpleui) ☁ test_migations python manage.py migrate
System check identified some issues:
WARNINGS:
app_1.Model1: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'.
HINT: Configure the DEFAULT_AUTO_FIELD setting or the App1Config.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'.
app_2.Model2: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'.
HINT: Configure the DEFAULT_AUTO_FIELD setting or the App2Config.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'.
Operations to perform:
Apply all migrations: admin, app_1, app_2, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying app_1.0001_initial... OK
Applying app_2.0001_initial... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying auth.0012_alter_user_first_name_max_length... OK
Applying sessions.0001_initial... OK
**Add blank=True to the scenes field of Model2**
(simpleui) ☁ test_migations python manage.py makemigrations
System check identified some issues:
WARNINGS:
app_1.Model1: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'.
HINT: Configure the DEFAULT_AUTO_FIELD setting or the App1Config.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'.
app_2.Model2: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'.
HINT: Configure the DEFAULT_AUTO_FIELD setting or the App2Config.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'.
Migrations for 'app_2':
app_2/migrations/0002_alter_model2_scenes.py
- Alter field scenes on model2
(simpleui) ☁ test_migations python manage.py migrate
System check identified some issues:
WARNINGS:
app_1.Model1: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'.
HINT: Configure the DEFAULT_AUTO_FIELD setting or the App1Config.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'.
app_2.Model2: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'.
HINT: Configure the DEFAULT_AUTO_FIELD setting or the App2Config.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'.
Operations to perform:
Apply all migrations: admin, app_1, app_2, auth, contenttypes, sessions
Running migrations:
Applying app_2.0002_alter_model2_scenes...Traceback (most recent call last):
File "/Users/donghao/test/test_migations/manage.py", line 22, in <module>
execute_from_command_line(sys.argv)
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line
utility.execute()
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/core/management/__init__.py", line 436, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/core/management/base.py", line 412, in run_from_argv
self.execute(*args, **cmd_options)
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/core/management/base.py", line 458, in execute
output = self.handle(*args, **options)
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/core/management/base.py", line 106, in wrapper
res = handle_func(*args, **kwargs)
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/core/management/commands/migrate.py", line 356, in handle
post_migrate_state = executor.migrate(
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/db/migrations/executor.py", line 135, in migrate
state = self._migrate_all_forwards(
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/db/migrations/executor.py", line 167, in _migrate_all_forwards
state = self.apply_migration(
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/db/migrations/executor.py", line 252, in apply_migration
state = migration.apply(state, schema_editor)
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/db/migrations/migration.py", line 132, in apply
operation.database_forwards(
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/db/migrations/operations/fields.py", line 235, in database_forwards
schema_editor.alter_field(from_model, from_field, to_field)
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/db/backends/base/schema.py", line 785, in alter_field
if not self._field_should_be_altered(old_field, new_field):
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/db/backends/base/schema.py", line 1520, in _field_should_be_altered
return self.quote_name(old_field.column) != self.quote_name(
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/db/backends/base/schema.py", line 204, in quote_name
return self.connection.ops.quote_name(name)
File "/Users/donghao/.virtualenvs/simpleui/lib/python3.9/site-packages/django/db/backends/mysql/operations.py", line 184, in quote_name
if name.startswith("`") and name.endswith("`"):
AttributeError: 'NoneType' object has no attribute 'startswith'
follow-up: 9 comment:8 by , 3 years ago
| Resolution: | → invalid |
|---|---|
| Status: | new → closed |
ForeignObject is an abstraction level, not a proper model field. You should use models.ForeignKey. If you're having trouble understanding how Django works, see TicketClosingReasons/UseSupportChannels for ways to get help.
follow-up: 10 comment:9 by , 3 years ago
Replying to Mariusz Felisiak:
ForeignObjectis an abstraction level, not a proper model field. You should usemodels.ForeignKey. If you're having trouble understanding how Django works, see TicketClosingReasons/UseSupportChannels for ways to get help.
We do not need to create a foreign key, we have used ForeignObject and currently only need to perform queries. Should this field not be generated in the migration record for the first time?
comment:10 by , 3 years ago
Replying to puc_dong:
Replying to Mariusz Felisiak:
ForeignObjectis an abstraction level, not a proper model field. You should usemodels.ForeignKey. If you're having trouble understanding how Django works, see TicketClosingReasons/UseSupportChannels for ways to get help.
We do not need to create a foreign key, we have used ForeignObject and currently only need to perform queries. Should this field not be generated in the migration record for the first time?
It seems that you have a niche use case. Trac is not a support channel, you can try to discuss this with folks on the Django Forum.
comment:11 by , 3 years ago
| Needs tests: | set |
|---|---|
| Patch needs improvement: | set |
| Resolution: | invalid |
| Status: | closed → new |
| Summary: | Change the properties of ForeignObject object, such as blank, null, and execute migrate to report an error → Migrations crashes when changing ForeignObject properties. |
| Triage Stage: | Unreviewed → Accepted |
After reconsideration I think we should fix this (check out similar report #25128).
comment:12 by , 3 years ago
| Has patch: | unset |
|---|---|
| Needs tests: | unset |
| Patch needs improvement: | unset |
follow-up: 14 comment:13 by , 3 years ago
A regression test:
-
tests/migrations/test_operations.py
diff --git a/tests/migrations/test_operations.py b/tests/migrations/test_operations.py index d4fc3e855a..6366ccd667 100644
a b class OperationTests(OperationTestBase): 2306 2306 operation.database_forwards(app_label, editor, new_state, project_state) 2307 2307 self.assertColumnExists(rider_table, "pony_id") 2308 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2309 2349 @skipUnlessDBFeature("supports_comments") 2310 2350 def test_alter_model_table_comment(self): 2311 2351 app_label = "test_almotaco"
comment:14 by , 3 years ago
Replying to Mariusz Felisiak:
A regression test:
tests/migrations/test_operations.py
diff --git a/tests/migrations/test_operations.py b/tests/migrations/test_operations.py index d4fc3e855a..6366ccd667 100644
a b class OperationTests(OperationTestBase): 2306 2306 operation.database_forwards(app_label, editor, new_state, project_state) 2307 2307 self.assertColumnExists(rider_table, "pony_id") 2308 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2309 2349 @skipUnlessDBFeature("supports_comments") 2310 2350 def test_alter_model_table_comment(self): 2311 2351 app_label = "test_almotaco"
I realized that there will be a default exclude behavior, and there are 6 queries that will be executed in database setup, so I modified the use case, and the 6 queries were not written directly.
This is my modification:https://github.com/django/django/pull/17240
comment:15 by , 3 years ago
| Has patch: | set |
|---|
comment:16 by , 3 years ago
| Owner: | changed from to |
|---|---|
| Status: | new → assigned |
| Triage Stage: | Accepted → Ready for checkin |
I'm not sure how to you reach a field without a
columnin migrations. I don't think you've explained the issue in enough detail to confirm a bug in Django, can you provide a sample project or test case that reproduces the issue?