#20955: Deep select_related to same model returns incorrect relationship
----------------------------------------------+--------------------
     Reporter:  zanuxzan                      |      Owner:  nobody
         Type:  Bug                           |     Status:  new
    Component:  Database layer (models, ORM)  |    Version:  master
     Severity:  Normal                        |   Keywords:
 Triage Stage:  Unreviewed                    |  Has patch:  0
Easy pickings:  0                             |      UI/UX:  0
----------------------------------------------+--------------------
 When using select_related on a deeply nested relationship and you have two
 (or possibly more) relationships that use the same model the SQL query is
 incorrect and thus the resulting model incorrect.

 This is best illustrated with an example:

 {{{#!python
 expect = Task.objects.get(pk=1)
 actual = Task.objects.select_related('creator__staffuser__staff',
 'owner__staffuser__staff').get(pk=1)

 if actual.creator.staffuser.staff != expect.creator.staffuser.staff:
     print "Creator Incorrect"
 if actual.owner.staffuser.staff != expect.owner.staffuser.staff:
     print "Owner Incorrect"
 }}}

 Outputs ```Owner Incorrect```

 == Test Project

 I've created a test project at
 https://github.com/alexhayes/deepselectrelated

 This test project has two tests that illustrates the difference in
 behaviour.

 == Issue Location

 It appears that this issue exists in the actual SQL statements that are
 being generated, as taken from my test project:

 {{{#!sql
          SELECT "deepselectrelated_task"."id",
 "deepselectrelated_task"."title", "deepselectrelated_task"."creator_id",
 "deepselectrelated_task"."owner_id",
                 "auth_user"."id", "auth_user"."password",
 "auth_user"."last_login", "auth_user"."is_superuser",
 "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name",
 "auth_user"."email", "auth_user"."is_staff", "auth_user"."is_active",
 "auth_user"."date_joined",
                 "deepselectrelated_staffuser"."user_ptr_id",
 "deepselectrelated_staffuser"."staff_id",
                 "deepselectrelated_staff"."id",
 "deepselectrelated_staff"."name",
                 T5."id", T5."password", T5."last_login",
 T5."is_superuser", T5."username", T5."first_name", T5."last_name",
 T5."email", T5."is_staff", T5."is_active", T5."date_joined",
                 T6."user_ptr_id", T6."staff_id",
                 "deepselectrelated_staff"."id",
 "deepselectrelated_staff"."name"
            FROM "deepselectrelated_task"
      INNER JOIN "auth_user" ON ( "deepselectrelated_task"."creator_id" =
 "auth_user"."id" )
 LEFT OUTER JOIN "deepselectrelated_staffuser" ON ( "auth_user"."id" =
 "deepselectrelated_staffuser"."user_ptr_id" )
 LEFT OUTER JOIN "deepselectrelated_staff" ON (
 "deepselectrelated_staffuser"."staff_id" = "deepselectrelated_staff"."id"
 )
      INNER JOIN "auth_user" T5 ON ( "deepselectrelated_task"."owner_id" =
 T5."id" )
 LEFT OUTER JOIN "deepselectrelated_staffuser" T6 ON ( T5."id" =
 T6."user_ptr_id" )
 }}}

 Note that there is only one join to the staff table and the following part
 of the SELECT query is incorrectly repeated:

 {{{#!sql
 "deepselectrelated_staff"."id", "deepselectrelated_staff"."name"
 }}}

 There should infact be two joins to the staff table and the SELECT should
 be as follows:

 {{{#!sql
          SELECT "deepselectrelated_task"."id",
 "deepselectrelated_task"."title", "deepselectrelated_task"."creator_id",
 "deepselectrelated_task"."owner_id",
                 "auth_user"."id", "auth_user"."password",
 "auth_user"."last_login", "auth_user"."is_superuser",
 "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name",
 "auth_user"."email", "auth_user"."is_staff", "auth_user"."is_active",
 "auth_user"."date_joined",
                 "deepselectrelated_staffuser"."user_ptr_id",
 "deepselectrelated_staffuser"."staff_id",
                 "deepselectrelated_staff"."id",
 "deepselectrelated_staff"."name",
                 T5."id", T5."password", T5."last_login",
 T5."is_superuser", T5."username", T5."first_name", T5."last_name",
 T5."email", T5."is_staff", T5."is_active", T5."date_joined",
                 T6."user_ptr_id", T6."staff_id",
                 T7."id", T7."name"
            FROM "deepselectrelated_task"
      INNER JOIN "auth_user" ON ( "deepselectrelated_task"."creator_id" =
 "auth_user"."id" )
 LEFT OUTER JOIN "deepselectrelated_staffuser" ON ( "auth_user"."id" =
 "deepselectrelated_staffuser"."user_ptr_id" )
 LEFT OUTER JOIN "deepselectrelated_staff" ON (
 "deepselectrelated_staffuser"."staff_id" = "deepselectrelated_staff"."id"
 )
      INNER JOIN "auth_user" T5 ON ( "deepselectrelated_task"."owner_id" =
 T5."id" )
 LEFT OUTER JOIN "deepselectrelated_staffuser" T6 ON ( T5."id" =
 T6."user_ptr_id" )
 LEFT OUTER JOIN "deepselectrelated_staff" T7 ON ( T6."staff_id" = T7."id"
 )
 }}}

-- 
Ticket URL: <https://code.djangoproject.com/ticket/20955>
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 post to this group, send email to django-updates@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/051.6c80da9da49157979c0425220a4d7516%40djangoproject.com.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to