【问题标题】:How to use a custom __init__ of an app engine Python model class properly?如何正确使用应用引擎 Python 模型类的自定义 __init__?
【发布时间】:2011-09-24 07:32:41
【问题描述】:

我正在尝试实施延迟的博客文章删除方案。因此,您可以在 2 分钟的时间范围内取消删除,而不是烦人的您确定吗?

我想跟踪使用 db.Model 类 (DeleteQueueItem) 时将删除的内容,因为我发现无法从队列中删除任务并怀疑我可以查询那里的内容。

创建 DeleteQueueItem 实体应自动设置 delete_when 属性并将任务添加到队列中。我使用博客文章的相对路径作为它们的 key_name 并希望在此处也将其用作 key_name。这让我有了一个自定义 init

class DeleteQueueItem(db.Model):
    """Model to keep track of items that will be deleted via task queue."""

    # URL path to the blog post is handled as key_name
    delete_when = db.DateTimeProperty()

    def __init__(self, **kwargs):
        delay = 120  # Seconds
        t = datetime.timedelta(seconds=delay)
        deadline = datetime.datetime.now() - t
        key_name = kwargs.get('key_name')

        db.Model.__init__(self, **kwargs)
        self.delete_when = deadline

        taskqueue.add(url='/admin/task/delete_page', 
                      countdown=delay,
                      params={'path': key_name})

这似乎有效,直到我尝试删除实体:

fetched_item = models.DeleteQueueItem.get_by_key_name(path)

这失败了:

TypeError: __init__() takes exactly 1 non-keyword argument (2 given)

我做错了什么?

【问题讨论】:

    标签: python google-app-engine


    【解决方案1】:

    通常,您不应尝试覆盖 Model 类的 init 方法。虽然可以做到正确,但正确的构造函数行为相当复杂,甚至可能在不同版本之间发生变化,从而破坏您的代码(尽管我们尽量避免这样做!)。造成这种情况的部分原因是您自己的代码必须使用构造函数来构造新模型,框架必须使用构造函数来重构从数据存储区加载的模型。

    更好的方法是使用工厂方法,而不是构造函数。

    此外,您可能希望在编写实体的同时添加任务,而不是在创建时添加。如果不这样做,您最终会遇到竞争条件:任务可能在您将新实体存储到数据存储之前执行!

    这是一个建议的重构:

    class DeleteQueueItem(db.Model):
        """Model to keep track of items that will be deleted via task queue."""
    
        # URL path to the blog post is handled as key_name
        delete_when = db.DateTimeProperty()
    
        @classmethod
        def new(cls, key_name):
            delay = 120  # Seconds
            t = datetime.timedelta(seconds=delay)
            deadline = datetime.datetime.now() - t
    
            return cls(key_name=key_name, delete_when=deadline)
    
        def put(self, **kwargs):
          def _tx():
            taskqueue.add(url='/admin/task/delete_page', 
                          countdown=delay,
                          params={'path': key_name},
                          transactional=True)
            return super(DeleteQueueItem, self).put(**kwargs)
          if not self.is_saved():
            return db.run_in_transaction(_tx)
          else:
            return super(DeleteQueueItem, self).put(**kwargs)
    

    【讨论】:

    • 太好了,谢谢!一个问题是,我很难想出一个优雅的解决方案,但延迟和 key_name 在 def _tx() 中不可用。
    • 哎呀,好点子。您可以将“延迟”定义为类成员(例如,在顶层),因为它看起来是一个常量,并且可以通过 self.key().name() 访问键名。
    • 开始工作了!班级成员的延迟很清楚,但我永远不会到达 self.key().name()。再次感谢!
    • 尼克,设置默认值怎么样?文档建议创建自己的 initcode.google.com/appengine/docs/python/datastore/…
    • 我看到 Will McCutchen 如何使用 super(ModelSubClass, self).__init__。设置生成的默认值的好方法。也许扩展上面链接的文档(参见构造函数>类属性>默认>注释)? stackoverflow.com/questions/2465675/…
    猜你喜欢
    • 1970-01-01
    • 2012-09-21
    • 1970-01-01
    • 1970-01-01
    • 2023-03-28
    • 2020-08-22
    • 2019-02-14
    • 2015-11-23
    • 1970-01-01
    相关资源
    最近更新 更多