【问题标题】:GAE: Changing the value of one entity when another entity's value changesGAE:当另一个实体的价值改变时改变一个实体的价值
【发布时间】:2014-08-22 16:19:23
【问题描述】:

假设我有两个数据存储模型:

class Author(ndb.Model):
    name = ndb.StringProperty()
    has_dog = ndb.BooleanProperty(default = False)

class Book(ndb.Model):
    title = ndb.StringProperty()
    author_key = ndb.KeyProperty() # key of associated Author entity

创建Book 实体时,会为其author_key 分配Author 实体的键。现在,我可以像这样查询每个作者的书籍列表:

books_per_author = Book.query(Book.author_key == author_key).fetch()

...然后像这样在 jinja 模板上渲染它们:

{% for book in books_per_author %}
    <h2>{{book.title}}</h2>
{% endfor %}

但是如果我还想在同一个模板中显示作者的has_dog 值怎么办?也许我可以像这样将数据标准化为Book 实体:

class Book(ndb.Model):
    title = ndb.StringProperty()
    author_key = ndb.KeyProperty()
    author_has_dog = ndb.BooleanProperty() # get this value from Author entity before book.put() happens

所以现在,当我们创建 Book 实体时,我们只需获取 Author 的 has_dog 值并将其保存在 Book 的 author_has_dog 属性中。问题解决了,我们可以这样做:

{% for book in books_per_author %}
    <h2>{{book.title}}</h2>
    <div>Has dog: {{book.author_has_dog}}</div>
{% endfor %}

问题:现在,如果我们突然改变Author 实体中has_dog 的值怎么办?我们如何有效地更改与该作者关联的许多 Book 实体中的 author_has_dog 值?

编辑以包含NewBook处理程序:

class NewBook(BaseHandler):
    def get(self):
        title = self.request.get('title')
        author_key = self.get_author_key()
        self.render('booklist-per-author.html', title = title, author_key = author_key)

【问题讨论】:

    标签: python google-app-engine jinja2 google-cloud-datastore app-engine-ndb


    【解决方案1】:

    更好的选择是只引用书中的作者:

    {% for book in books_per_author %}
        <h2>{{book.title}}</h2>
        <div>Has dog: {{book.author_key.get().has_dog}}</div>
    {% endfor %}
    

    并且不需要更新处理。

    您可能希望触发对作者的预取;在您的视图代码中,在渲染模板之前,调用:

    book.author_key.get_async()
    

    为作者对象启动异步获取,当您的模板被渲染时,它可能已经到达。

    【讨论】:

    • 谢谢@MartijnPieters。这正是我正在寻找的。但是,我对_async的使用不熟悉(我只知道它背后的基本理论)。如果不是太麻烦,您能解释一下book.author_key.bet_async() 如何适合我的代码吗?我已编辑问题以包含NewBook 处理程序。无论如何,非常感谢。
    • @haopei:如果您从作者密钥开始,您不妨预取它;致电author_key.get_async()。该调用立即返回,并指示应用引擎继续获取数据,因为您稍后会想要访问它。如果您随后尝试使用.get() 再次访问相同的密钥,它将使用预取的数据或加入已经运行的异步提取。这就像在你路过书店时派一个跑步者去买菜,当你到超市时,跑步者已经为你完成了部分或全部购物。
    • 假设这里的Author 模型还包括其他属性,例如agedate_of_birthgender。为了检索所有这些,我可以对所有属性进行单独的调用,例如 book.author_key.get().gender。这是否意味着我要为每个get() 花费一个数据存储读取?我担心这里的可扩展性。
    • @haopei:不,这些调用将重用缓存数据。当然,您总是可以先将作者存储在局部变量中。
    【解决方案2】:

    使用pre_put function,当您put()作者实体时自动运行:

    class Author(ndb.Model):
        name = ndb.StringProperty()
        has_dog = ndb.BooleanProperty(default = False)
    
        def _pre_put_hook(self):
            # update books ...
            books = Book.query(Book.author_key == author_key).fetch()
            for book in books:
                book.author_has_dog = self.has_dog
            ndb.put_multi(books)
    

    如果作者倾向于拥有大量书籍,这不会很好地扩展,因此您可能希望卸载到定期检查不一致的 cron 作业,但这可能会导致数据变得陈旧。

    或者,正如 Martjn 在他的回答中提到的那样,每次只看作者。

    【讨论】:

    • 感谢格温的回答。你是对的 - 这个不是很可扩展:) 你让我注意到pre_put 函数,它看起来对我正在尝试做的许多其他事情很有用。
    猜你喜欢
    • 2023-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-09
    • 2015-01-01
    • 1970-01-01
    相关资源
    最近更新 更多