Caching is hard. Caching as an automated 'layer' is much harder. If it were possible to cache in a general way databases would do it already. Adding a 'caching layer' is opening a gate into hell. The things is, at first it will be fine. Thousands of engineer hours and hundreds of subtle bugs later, you'll (if you're wise) realize that opening that door let out many demons, you just didn't have the eyes to see them at the time.
"There are only two hard things in Computer Science: cache invalidation and naming things." -- Phil Karlton
Adding read slaves is a great on many levels. Adding slaves, however, is not without overhead and stale data/programming complexity exists there too.
It's nice that MySQL caches queries, but it doesn't solve the same problem that an application-level cache does.
In some level, they DO do it already.
Beyond that level, they don't do it because it's application specific.
My changes ( https://github.com/theatlantic/django-cache-machine/ ) inspect the ORDER BY clauses (if there's a limit or offset) and WHERE constraints in the query and saves the list of "model-invaliding columns" (i.e., columns which, when changed, should broadly invalidate queries on the model) in a key in the cache. It also associates these queries with a model-flush list. Then, in a pre_save signal, it checks whether any of those columns have changed and marks the instance to invalidate the associated model flush list when it gets passed into the post_save signal. We have these changes up on a few of our sites and, if all goes well, we're looking to move the invalidation at the column level to make the cache-hit ratio even higher.
Really if you're caching SQL queries and such, you really, really should be doing little to no modification of cached data - this library makes it seem "easy" which it's not.
That points makes all the difference, in my mind. To your point about excessive invalidation, that depends on your Read/Write workload.
The top commenter in OP gave a great rundown of these projects and their evaluation of them at YCharts at a Django NYC meetup a month-ish ago; I'm sure his slides are available on the nyc django site somewhere.
All of these projects "automatically" manage cache for querysets, but they do it different ways, and can be susceptible to poor performance under different usage patterns.
From what I can tell, JC adds the lowest amt of overhead to cache misses and hits, and uses the simplest (it's mildly sophisticated, but still straightforward) management algorithm. It's the only one that works fine when using UPDATE queries that do not mention row ids, and (as a result) is the one that most greedily invalidates on writes.
The others are fine projects run by smart people, and depending on your site's situation, I'd recommend some of them over johnny-cache. It's a good idea to evaluate them all, as they certainly did at YCharts (his section on JC was very accurate), and as OP seems to have done.