Вот моя проблема:
class City(Model):
name = StringProperty()
class Author(Model):
name = StringProperty()
city = ReferenceProperty(City)
class Post(Model):
author = ReferenceProperty(Author)
content = StringProperty()
Код не важен ... это шаблон django:
{% for post in posts %}
<div>{{post.content}}</div>
<div>by {{post.author.name}} from {{post.author.city.name}}</div>
{% endfor %}
Теперь предположим, что я получаю первые 100 сообщений, используя Post.all().fetch(limit=100)
, и передаю этот список шаблону - что происходит?
Это дает 200 больше хранилище данных - 100, чтобы получить каждого автора, 100, чтобы получить город каждого автора.
Это вполне понятно, на самом деле, поскольку пост содержит только ссылку на автора, а автор имеет только ссылку на город. Аксессор __get__
на объектах post.author
и author.city
прозрачно выполняет получение и получение данных обратно (см. этот вопрос).
Некоторые способы обойти это
- Используйте
Post.author.get_value_for_datastore(post)
, чтобы собрать ключи автора (см. Ссылку выше), а затем выполнить пакет, чтобы получить их все - проблема здесь в том, что нам нужно пересоздать объект данных шаблона ... что-то, что нуждается в дополнительном коде и обслуживании для каждой модели и обработчика.
- Напишите метод доступа, скажем,
cached_author
, который сначала проверяет memcache для автора и возвращает это - проблема здесь в том, что post.cached_author будет вызываться 100 раз, что, вероятно, может означать 100 вызовов memcache.
- Удерживайте статический ключ на карте объекта (и обновляйте его, возможно, раз в пять минут), если данные не должны быть очень актуальными. Аксессор
cached_author
может просто обратиться к этой карте.
Все эти идеи требуют дополнительного кода и обслуживания, и они не очень прозрачны. Что если бы мы могли сделать
@prefetch
def render_template(path, data)
template.render(path, data)
Оказывается, мы можем ... крючки и Модуль инструментов Гвидо оба доказывают это. Если метод @prefetch
обертывает шаблонный рендер, фиксируя, какие ключи запрашиваются, мы можем (по крайней мере, до одного уровня глубины) захватить, какие ключи запрашиваются, вернуть фиктивные объекты и выполнить пакетное получение их. Это может повторяться для всех уровней глубины, пока новые ключи не запрашиваются. Финальный рендер может перехватывать объекты get и возвращать объекты с карты.
Это может изменить в общей сложности 200 на 3 , прозрачно и без дополнительного кода. Не говоря уже о значительном сокращении потребности в memcache и помощи в ситуациях, когда memcache не может быть использован.
Проблема в том, что я не знаю, как это сделать (пока). Прежде чем я попробую, кто-нибудь еще сделал это? Или кто-нибудь хочет помочь? Или вы видите огромный недостаток в плане?