It’s a reasonable ORM, but doesn’t compare to SQLAlchemy.
A dirty-tracking mode would be great. This is really useful if you want to have code that builds up a list of sub-objects, and then _maybe_ commits them, but maybe rolls back after creating most of the Python objects. The naive Python you want to write is `for i in range(10): foo.bars.add(Bar(i))` which in the Django ORM triggers a DB write for each bar, even if you error out on the 10th Bar.
As you say, another area where the ORM can bite you is the always-on foreign-key-traversal logic. So if you have loaded model foo, then accessing foo.bar will trigger a SQL query under the covers, if you didn't select_related/prefetch_related properly. This is great for simple use-cases and exploratory coding, but can make it very easy to write code with horrendous performance. Naively-coded views often have hundreds of DB lookups and it's hard to track down where these are coming from when the app is complex and especially when you've got _some_ *_related in place, but you're missing a prefetch somewhere.
The request-handling and template rendering is in my mind the weakpoint.