Django Adminサイトの一覧画面のSQLはINNER JOINを利用していた

Django Adminサイトの一覧画面からレコードが見えないケースもある の続き。

タイトルに書いた通り、INNER JOINを利用していたのでDjango Adminサイトの一覧画面で見えないという状況だった。

確認した内容

次のSQLを利用して一覧画面に出力するデータを取得していた。

SELECT "company_employee"."id",
       "company_employee"."name",
       "company_employee"."department_id",
       "company_department"."id",
       "company_department"."name"
  FROM "company_employee"
 INNER JOIN "company_department"
    ON ("company_employee"."department_id" = "company_department"."id")
 ORDER BY "company_employee"."id" DESC
 
 Connection: default

Pythonのトレースは以下。

 
 /Users/tatsuya/djangoTaskApp/venv/lib/python3.12/site-packages/django/contrib/staticfiles/handlers.py in __call__(80)
   return self.application(environ, start_response)
 
 /Users/tatsuya/djangoTaskApp/venv/lib/python3.12/site-packages/django_browser_reload/middleware.py in __call__(48)
   response = self.get_response(request)
 
 /Users/tatsuya/djangoTaskApp/venv/lib/python3.12/site-packages/django/contrib/admin/options.py in wrapper(688)
   return self.admin_site.admin_view(view)(*args, **kwargs)
 
 /Users/tatsuya/djangoTaskApp/venv/lib/python3.12/site-packages/django/views/decorators/cache.py in _wrapper_view_func(62)
   response = view_func(request, *args, **kwargs)
 
 /Users/tatsuya/djangoTaskApp/venv/lib/python3.12/site-packages/django/contrib/admin/sites.py in inner(242)
   return view(request, *args, **kwargs)
 
 /Users/tatsuya/djangoTaskApp/venv/lib/python3.12/site-packages/django/contrib/admin/options.py in changelist_view(2065)
   "selection_note": _("0 of %(cnt)s selected") % {"cnt": len(cl.result_list)},
 

確かに、INNER JOINであれば両方のテーブルにレコードがないと表示されないので納得の結果だった。 これをDjangoオンラインミートアップで飛び入り発表させてもらったところ、主催者のkyさん、tokibitoさんにも面白く聞いてもらうことができた。

なお、その他同時に実行されているSQLとトレース情報は以下。

SELECT COUNT(*) AS "__count"
  FROM "company_employee" 2 similar queries.  Duplicated 2 times.      0.26  
Sel Expl
Connection: default
/Users/tatsuya/djangoTaskApp/venv/lib/python3.12/site-packages/django/contrib/staticfiles/handlers.py in __call__(80)
  return self.application(environ, start_response)

/Users/tatsuya/djangoTaskApp/venv/lib/python3.12/site-packages/django_browser_reload/middleware.py in __call__(48)
  response = self.get_response(request)

/Users/tatsuya/djangoTaskApp/venv/lib/python3.12/site-packages/django/contrib/admin/options.py in wrapper(688)
  return self.admin_site.admin_view(view)(*args, **kwargs)

/Users/tatsuya/djangoTaskApp/venv/lib/python3.12/site-packages/django/views/decorators/cache.py in _wrapper_view_func(62)
  response = view_func(request, *args, **kwargs)

/Users/tatsuya/djangoTaskApp/venv/lib/python3.12/site-packages/django/contrib/admin/sites.py in inner(242)
  return view(request, *args, **kwargs)

/Users/tatsuya/djangoTaskApp/venv/lib/python3.12/site-packages/django/contrib/admin/options.py in changelist_view(1926)
  cl = self.get_changelist_instance(request)

/Users/tatsuya/djangoTaskApp/venv/lib/python3.12/site-packages/django/contrib/admin/options.py in get_changelist_instance(836)
  return ChangeList(

/Users/tatsuya/djangoTaskApp/venv/lib/python3.12/site-packages/django/contrib/admin/views/main.py in __init__(123)
  self.get_results(request)

/Users/tatsuya/djangoTaskApp/venv/lib/python3.12/site-packages/django/contrib/admin/views/main.py in get_results(279)
  result_count = paginator.count

/Users/tatsuya/djangoTaskApp/venv/lib/python3.12/site-packages/django/core/paginator.py in count(93)
  return c()
  

もう1つ、同じSQLを発行しているが違う箇所が起点のものがあった。 上記はページネーション、下記は件数取得でそれぞれ別物のようだった。

SELECT COUNT(*) AS "__count"
  FROM "company_employee" 2 similar queries.  Duplicated 2 times.      0.15  
Sel Expl
Connection: default
/Users/tatsuya/djangoTaskApp/venv/lib/python3.12/site-packages/django/contrib/staticfiles/handlers.py in __call__(80)
  return self.application(environ, start_response)

/Users/tatsuya/djangoTaskApp/venv/lib/python3.12/site-packages/django_browser_reload/middleware.py in __call__(48)
  response = self.get_response(request)

/Users/tatsuya/djangoTaskApp/venv/lib/python3.12/site-packages/django/contrib/admin/options.py in wrapper(688)
  return self.admin_site.admin_view(view)(*args, **kwargs)

/Users/tatsuya/djangoTaskApp/venv/lib/python3.12/site-packages/django/views/decorators/cache.py in _wrapper_view_func(62)
  response = view_func(request, *args, **kwargs)

/Users/tatsuya/djangoTaskApp/venv/lib/python3.12/site-packages/django/contrib/admin/sites.py in inner(242)
  return view(request, *args, **kwargs)

/Users/tatsuya/djangoTaskApp/venv/lib/python3.12/site-packages/django/contrib/admin/options.py in changelist_view(1926)
  cl = self.get_changelist_instance(request)

/Users/tatsuya/djangoTaskApp/venv/lib/python3.12/site-packages/django/contrib/admin/options.py in get_changelist_instance(836)
  return ChangeList(

/Users/tatsuya/djangoTaskApp/venv/lib/python3.12/site-packages/django/contrib/admin/views/main.py in __init__(123)
  self.get_results(request)

/Users/tatsuya/djangoTaskApp/venv/lib/python3.12/site-packages/django/contrib/admin/views/main.py in get_results(283)
  full_result_count = self.root_queryset.count()