SQL has a lot of issues making working with it painful for library authors. Django could not be so useful without an ORM. Not to say it should be avoided at all cost, but a layer above is often required and should be easily available if desired.
How you migrate the addition of an extension on PostgreSQL?
By declaring that I want to use some library/application module/crate that requires it and having this thing generating whatever is required for it to work. It might use SQL, it might not (Mysql may use percona instead of just sql), I don’t need to know that. It might do different things depending on a database I am using or its version and configuration.
Or the removal of 10 tables, to be put on one that need a massive SQL rewrite on the middle?
Its trivial with Django: add new table (make migrations), add data migration, update the app to use it , remove 10 tables (make migrations), done. 2 of 3 steps will have automatically generated migrations forward and back. With “write your sql”. its a lot more work that computers do better than people.
“Not write SQL” is nice for BASIC stuff.
Yes. But there are many applications that do a lot of basic stuff, having it done in automated way makes things a lot easier to change. Writing “basic” migration in a database-agnostic way is actually anything but basic.
A 2-layer or 3-layer design can work well
Conceptually maybe, but in practice you will have database driver integrated at all levels, because layers 2 and 3 require features layer 1 doesn’t need or knows about.