#36034: ForeignKey to CompositePrimaryKey crashes.
-------------------------------------+-------------------------------------
               Reporter:  Mariusz    |          Owner:  Mariusz Felisiak
  Felisiak                           |
                   Type:  Bug        |         Status:  assigned
              Component:  Database   |        Version:  dev
  layer (models, ORM)                |
               Severity:  Release    |       Keywords:
  blocker                            |
           Triage Stage:             |      Has patch:  0
  Unreviewed                         |
    Needs documentation:  0          |    Needs tests:  0
Patch needs improvement:  0          |  Easy pickings:  0
                  UI/UX:  0          |
-------------------------------------+-------------------------------------
 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.
-- 
Ticket URL: <https://code.djangoproject.com/ticket/36034>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-updates+unsubscr...@googlegroups.com.
To view this discussion visit 
https://groups.google.com/d/msgid/django-updates/01070193eef76bce-d1703317-9482-4998-91b9-259d2580e1ef-000000%40eu-central-1.amazonses.com.

Reply via email to