This idea of "transaction rollback in test teardown because performance" has a life of its own. The recommended base class for django unit tests (informally recommended, via code comments, not actual docs) uses transaction rollbacks instead of table truncation [0].
On top of this, I think, db truncation gets mixed up with table truncation sometimes too. For example, from OP:
> The time taken to clean the database is usually proportional to the number of tables
... only if you're truncating the whole db and re-initializing the schema, no?
And people sometimes actually do clear the whole db between tests! One unfortunate reason being functionally necessary data migrations that are mixed up with schema-producing migrations, meaning truncating tables doesn't take you back to "zero".
[0]: https://docs.djangoproject.com/en/2.2/_modules/django/test/t...