I'm the opposite, I would rather write SQL than "resorting to" ORM queries, which is why my favourite libraries are aiosql[1] in Python, Hugsql[2] in Clojure and similar: write the queries as SQL in .sql files, which then get exposed as functions to your code.
Dynamic SQL.
I’ve written aiosql functions that use dynamic SQL (via plpgsql) just fine.
https://www.hugsql.org/using-hugsql/composability/snippets
But the vast majority of SQL I’ve worked with simply doesn’t need it. But even when you do, the situation has never been any worse than it is with ORM’s for me.
As a comparison, DX-wise this is no safer and is indeed very similar to the usual idiom in Go for example, where you just concatenate (pre-interpolated) SQL strings. But when you actually want the compiler to prove the correctness of your queries even in a rudimentary way, these .sql file solutions usually (if not, everytime) fail to provide the necessary external checker that processes templates and uses an accurate model of your database and SQL to verify that all used combinations make sense.
The closest thing to a proper take on this I've seen is https://github.com/andywer/squid with https://github.com/andywer/postguard which, although the SQL is inlined in the code, it uses the right approach for verifying correctness as far as I could tell in the little time I experimented with it.
There is also a mindset problem that's pretty serious in the ruby world, developers would use activerecord and load the universe, rather than running a select query carefully narrowed to the specific needs. on top of this, often there are entities that are the result of joining tables, but these are never surfaced with ORMs (activerecord specifically), since the concept of an entity from a query joined with multiple tables doesn't exist except in the limited fashion of a view.