Follow up to my own message. I found a way of testing this that I think everyone should look at:
$ ./manage.py shell
Python 3.8.5 (default, Jul 21 2020, 10:48:26)
[Clang 11.0.3 (clang-1103.0.32.62)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> import logging
>>> l = logging.getLogger('django.db.backends')
>>> l.setLevel(logging.DEBUG)
>>> l.addHandler(logging.StreamHandler())
This will allow you to see what SQL is actually being run. That way you can do things like comparing which statement is faster when you do things side by side.
>>> from django.contrib.auth.models import User
>>> qs = User.objects.all()
>>> val = qs.earliest('date_joined') if qs.exists() else None
(0.000) SELECT (1) AS "a" FROM "auth_user" LIMIT 1; args=()
(0.026) SELECT "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" FROM "auth_user" ORDER BY "auth_user"."date_joined" ASC LIMIT 1; args=()
>>> qs = User.objects.all() # Don’t use the cache
>>> val = qs.earliest('date_joined') if bool(qs) else None
(0.024) SELECT "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" FROM "auth_user"; args=()
(0.000) SELECT "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" FROM "auth_user" ORDER BY "auth_user"."date_joined" ASC LIMIT 1; args=()
This is a new install with only one user. Your numbers are likely to be much higher and have significant differences.