Opened 4 hours ago
#36034 assigned Bug
ForeignKey to CompositePrimaryKey crashes.
Reported by: | Mariusz Felisiak | Owned by: | Mariusz Felisiak |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | dev |
Severity: | Release blocker | Keywords: | |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
I've created a sample project that tries to create a foreign key to the table with CompositePrimaryKey
:
class Release(models.Model): pk = models.CompositePrimaryKey("version", "name") version = models.IntegerField() name = models.CharField(max_length=20) class RefRelease(models.Model): release = models.ForeignKey("Release", models.CASCADE)
It doesn't work (because #35956 is not implemented):
$ python manage.py sqlmigrate test_one 0001 BEGIN; -- -- Create model Release -- CREATE TABLE "test_one_release" ("version" integer NOT NULL, "name" varchar(20) NOT NULL, PRIMARY KEY ("version", "name")); -- -- Create model RefRelease -- CREATE TABLE "test_one_refrelease" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT); CREATE INDEX "test_one_refrelease_release_id_f24095be" ON "test_one_refrelease" ("release_id"); COMMIT;
FOREIGN KEY
has not been created in "test_one_refrelease"
:
$ python manage.py dbshell sqlite> pragma table_info(test_one_refrelease); 0|id|INTEGER|1||1 sqlite> pragma table_info(test_one_release); 0|version|INTEGER|1||1 1|name|varchar(20)|1||2 sqlite>
and any attempt to create a new object crashes:
$ python manage.py shell >>> from test_one.models import * >>> r = Release.objects.create(version=3, name="y") >>> RefRelease.objects.create(release=r) Traceback (most recent call last): File "/django/django/db/backends/utils.py", line 105, in _execute return self.cursor.execute(sql, params) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/django/django/db/backends/sqlite3/base.py", line 360, in execute return super().execute(query, params) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ sqlite3.OperationalError: table test_one_refrelease has no column named release_id The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/django/django/db/backends/utils.py", line 134, in debug_sql yield File "/django/django/db/backends/utils.py", line 122, in execute return super().execute(sql, params) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/django/django/db/backends/utils.py", line 79, in execute return self._execute_with_wrappers( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/django/django/db/backends/utils.py", line 92, in _execute_with_wrappers return executor(sql, params, many, context) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/django/django/db/backends/utils.py", line 100, in _execute with self.db.wrap_database_errors: File "/django/django/db/utils.py", line 91, in __exit__ raise dj_exc_value.with_traceback(traceback) from exc_value File "/django/django/db/backends/utils.py", line 105, in _execute return self.cursor.execute(sql, params) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/django/django/db/backends/sqlite3/base.py", line 360, in execute return super().execute(query, params) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ django.db.utils.OperationalError: table test_one_refrelease has no column named release_id During handling of the above exception, another exception occurred: Traceback (most recent call last): File "<console>", line 1, in <module> File "/django/django/db/models/manager.py", line 87, in manager_method return getattr(self.get_queryset(), name)(*args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/django/django/db/models/query.py", line 663, in create obj.save(force_insert=True, using=self.db) File "/django/django/db/models/base.py", line 901, in save self.save_base( File "/django/django/db/models/base.py", line 1007, in save_base updated = self._save_table( ^^^^^^^^^^^^^^^^^ File "/django/django/db/models/base.py", line 1170, in _save_table results = self._do_insert( ^^^^^^^^^^^^^^^^ File "/django/django/db/models/base.py", line 1211, in _do_insert return manager._insert( ^^^^^^^^^^^^^^^^ File "/django/django/db/models/manager.py", line 87, in manager_method return getattr(self.get_queryset(), name)(*args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/django/django/db/models/query.py", line 1849, in _insert return query.get_compiler(using=using).execute_sql(returning_fields) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/django/django/db/models/sql/compiler.py", line 1891, in execute_sql cursor.execute(sql, params) File "/django/django/db/backends/utils.py", line 121, in execute with self.debug_sql(sql, params, use_last_executed_query=True): File "/usr/local/lib/python3.12/contextlib.py", line 158, in __exit__ self.gen.throw(value) File "/django/django/db/backends/utils.py", line 139, in debug_sql sql = self.db.ops.last_executed_query(self.cursor, sql, params) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/django/django/db/backends/sqlite3/operations.py", line 178, in last_executed_query params = self._quote_params_for_last_executed_query(params) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/django/django/db/backends/sqlite3/operations.py", line 167, in _quote_params_for_last_executed_query return cursor.execute(sql, params).fetchone() ^^^^^^^^^^^^^^^^^^^^^^^^^^^ sqlite3.ProgrammingError: Error binding parameter 1: type 'tuple' is not supported
IMO, we should at least raise an error (system check) in such cases. For now, migrations are proceeding without any indication that anything went wrong.
Note:
See TracTickets
for help on using tickets.