如何正确使用app引擎Python模型类的自定义__init__?

时间:2010-07-19 09:30:09

标签: python google-app-engine

我正在尝试实施延迟的博客帖子删除方案。因此,不是恼人的你确定吗?,你得到2分钟的时间框架来取消删除。

我想跟踪什么将被删除当使用db.Model类( DeleteQueueItem )时,我发现无法从队列中删除任务并怀疑我可以查询那里有什么。< / p>

创建 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)

我做错了什么?

1 个答案:

答案 0 :(得分:15)

通常,您不应尝试覆盖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)