#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 [email protected].
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