Para almacenar en caché un volcado directo de un solo objeto ya cargado, sí, no gana nada o casi nada. Eso no es lo que esos ejemplos están describiendo: están describiendo una jerarquía, donde cualquier cambio a algo más bajo también debería desencadenar una actualización de todo lo que está más arriba en la jerarquía.
El primer ejemplo, del blog 37signals, se usa Project -> Todolist -> Todocomo jerarquía. Un ejemplo poblado podría verse así:
Project: Foo (last_modified: 2014-05-10)
Todolist: Bar1 (last_modified: 2014-05-10)
Todo: Bang1 (last_modified: 2014-05-09)
Todo: Bang2 (last_modified: 2014-05-09)
Todolist: Bar2 (last_modified: 2014-04-01)
Todo: Bang3 (last_modified: 2014-04-01)
Todo: Bang4 (last_modified: 2014-04-01)
Entonces, digamos que Bang3se actualizó. Todos sus padres también se actualizan:
Project: Foo (last_modified: 2014-05-16)
Todolist: Bar2 (last_modified: 2014-05-16)
Todo: Bang3 (last_modified: 2014-05-16)
Luego, cuando llega el momento de renderizar, la carga Projectdesde la base de datos es básicamente inevitable. Necesitas un punto para comenzar. Sin embargo, debido a que last_modifiedes un indicador de todos sus elementos secundarios , eso es lo que utiliza como clave de caché antes de intentar cargar los elementos secundarios.
Si bien las publicaciones del blog usan plantillas separadas, las agruparé en una sola. Con suerte, ver la interacción completa en un lugar lo hará un poco más claro.
Entonces, la plantilla de Django podría verse así:
{% cache 9999 project project.cache_key %}
<h2>{{ project.name }}<h2>
<div>
{% for list in project.todolist.all %}
{% cache 9999 todolist list.cache_key %}
<ul>
{% for todo in list.todos.all %}
<li>{{ todo.body }}</li>
{% endfor %}
</ul>
{% endcache %}
{% endfor %}
</div>
{% endcache %}
Digamos que pasamos un proyecto cuyo cache_keytodavía existe en el caché. Debido a que propagamos los cambios a todos los objetos relacionados al padre, el hecho de que esa clave en particular aún exista significa que todo el contenido renderizado se puede extraer de la memoria caché.
Si ese Proyecto en particular acaba de actualizarse, por ejemplo, como en el caso Fooanterior, tendrá que representar a sus hijos, y solo entonces ejecutará la consulta para todos los Todolistas para ese Proyecto. Del mismo modo para un Todolist específico: si la clave_caché de esa lista existe, entonces los todos dentro de ella no han cambiado, y todo se puede extraer de la caché.
Observe también cómo no estoy usando todo.cache_keyen esta plantilla. No vale la pena, ya que como dices en la pregunta, bodyya se ha retirado de la base de datos. Sin embargo, los accesos a la base de datos no son la única razón por la que puede almacenar algo en caché. Por ejemplo, tomar texto de marcado sin formato (como lo que escribimos en los cuadros de preguntas / respuestas en StackExchange) y convertirlo a HTML puede llevar suficiente tiempo para que el resultado del almacenamiento en caché sea más eficiente.
Si así fuera, el bucle interno de la plantilla podría verse más así:
{% for todo in list.todos.all %}
{% cache 9999 todo todo.cache_key %}
<li>{{ todo.body|expensive_markup_parser }}</li>
{% endcache %}
{% endfor %}
Entonces, para reunir todo, volvamos a mis datos originales en la parte superior de esta respuesta. Si suponemos:
- Todos los objetos habían sido almacenados en caché en su estado original.
Bang3 acaba de actualizarse
- Estamos renderizando la plantilla modificada (incluida
expensive_markup_parser)
Entonces así es como se cargaría todo:
Foo se recupera de la base de datos
Foo.cache_key (2014-05-16) no existe en el caché
Foo.todolists.all()se consulta: Bar1y Bar2se recuperan de la base de datos
Bar1.cache_key(2014-05-10) ya existe en el caché ; recuperarlo y sacarlo
Bar2.cache_key (2014-05-16) no existe en el caché
Bar2.todos.all()se consulta: Bang3y Bang4se recuperan de la base de datos
Bang3.cache_key (2014-05-16) no existe en el caché
{{ Bang3.body|expensive_markup_parser }} se representa
Bang4.cache_key(2014-04-01) ya existe en el caché ; recuperarlo y sacarlo
Los ahorros del caché en este pequeño ejemplo son:
- Se evitó el impacto en la base de datos:
Bar1.todos.all()
expensive_markup_parserevitadas 3 veces: Bang1, Bang2yBang4
Y, por supuesto, la próxima vez que Foo.cache_keyse vea , se encontrará, por lo que el único costo para renderizar es recuperar Foosolo de la base de datos y consultar el caché.