【问题标题】:How do I define a unique property for a Model in Google App Engine?如何在 Google App Engine 中为模型定义唯一属性?
【发布时间】:2009-07-26 21:41:29
【问题描述】:

我需要一些属性是独一无二的。我怎样才能做到这一点?

unique=True之类的吗?

我正在使用 Google App Engine for Python。

【问题讨论】:

  • Blixt,编辑以使标题说应用引擎很好,因为许多应用引擎问题与语言无关,但问题应该在某个地方提到,发帖人对 Python 答案感兴趣,而不是 Java 答案。
  • 你是对的。我之前也考虑过添加它,但认为 Java 解决方案也会将 OP 指向正确的方向。但我认为 GAE 的 Java 和 Python 版本的不同足以使这个问题与 Python 相关。

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


【解决方案1】:

Google 提供了执行此操作的功能:

http://code.google.com/appengine/docs/python/datastore/modelclass.html#Model_get_or_insert

Model.get_or_insert(key_name, **kwds)

尝试使用给定的键名获取模型种类的实体。如果存在,get_or_insert() 会简单地返回它。如果它不存在,则创建、存储并返回具有 kwds 中给定种类、名称和参数的新实体。

get 和随后的(可能的)put 被包装在一个事务中以确保原子性。这意味着 get_or_insert() 永远不会覆盖现有实体,并且当且仅当不存在具有给定种类和名称的实体时才会插入新实体。

换句话说,get_or_insert() 等价于这个 Python 代码:

def txn():
  entity = MyModel.get_by_key_name(key_name, parent=kwds.get('parent'))
  if entity is None:
    entity = MyModel(key_name=key_name, **kwds)
    entity.put()
  return entity
return db.run_in_transaction(txn)

参数:

key_name 实体键的名称 **kwds 如果具有指定键名的实例不存在,则传递给模型类的构造函数的关键字参数。如果所需实体有父级,则需要 parent 参数。

注意:get_or_insert() 不接受 RPC 对象。

该方法返回代表所请求实体的模型类的一个实例,无论它是否存在或由该方法创建。与所有数据存储操作一样,如果事务无法完成,此方法会引发 TransactionFailedError。

【讨论】:

    【解决方案2】:

    没有用于确保值唯一的内置约束。但是,您可以这样做:

    query = MyModel.all(keys_only=True).filter('unique_property', value_to_be_used)
    entity = query.get()
    if entity:
        raise Exception('unique_property must have a unique value!')
    

    我使用keys_only=True 是因为它会通过不为实体获取数据而略微提高性能。

    更有效的方法是使用没有字段的单独模型,其键名由属性名 + 值组成。然后你可以使用get_by_key_name 来获取这些复合键名称中的一个或多个,如果你得到一个或多个 not-None 值,你就知道有重复值(并检查哪些值不是 None,你会知道哪些不是唯一的。)


    正如 onebyone 在 cmets 中提到的那样,这些方法 - 按其先入为主的性质 - 存在并发问题的风险。理论上,可以在检查现有值后立即创建实体,然后检查后的代码仍将执行,从而导致重复值。为防止这种情况,您必须使用事务:Transactions - Google App Engine


    如果您要检查具有交易的所有实体的唯一性,则必须使用第一种方法将所有实体放在同一个组中,这将非常低效。对于交易,使用第二种方法,如下所示:

    class UniqueConstraint(db.Model):
        @classmethod
        def check(cls, model, **values):
            # Create a pseudo-key for use as an entity group.
            parent = db.Key.from_path(model.kind(), 'unique-values')
    
            # Build a list of key names to test.
            key_names = []
            for key in values:
                key_names.append('%s:%s' % (key, values[key]))
    
            def txn():
                result = cls.get_by_key_name(key_names, parent)
                for test in result:
                    if test: return False
                for key_name in key_names:
                    uc = cls(key_name=key_name, parent=parent)
                    uc.put()
                return True
    
            return db.run_in_transaction(txn)
    

    UniqueConstraint.check(...) 将假定每个键/值对都必须唯一才能返回成功。该事务将为每个模型类型使用一个实体组。这样,事务同时对多个不同的字段是可靠的(仅对于一个字段,这会简单得多。)此外,即使您在一个或多个模型中具有相同名称的字段,它们也不会与彼此。

    【讨论】:

    • 可以将这些放在事务中,但我将把它留给 OP。
    • @onebyone:我用代码更新了我的帖子,以显示我对第二种方法的意思。它确实使用了非常广泛的实体组,但我希望他每个模型每秒插入 puts 的次数不会超过 5 个。
    • 我的理解是否正确,这个为每个类创建一个实体组,将插入速度限制为每秒一个?对所有被检查的属性进行哈希并将其作为字符串添加到“唯一值”中会引入任何不一致,从而产生多个实体组?
    • @petr 如果您对同一实体种类上完全不同的属性集进行唯一检查,则添加哈希将提高性能。 但是,如果您有重叠的属性集,那么您可能会遇到不一致,是的。
    • 好的,我的想法是在注册过程中使用唯一约束来检查用户名的唯一性。将注册限制为每秒一个可能是一个相当大的问题,因此我添加了简单的散列 code。我看不出这可能是一个问题,因为相同的用户名应该映射到相同的伪密钥,从而保持一致性。难道我做错了什么? (概念上)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-24
    • 1970-01-01
    • 1970-01-01
    • 2017-08-22
    • 1970-01-01
    相关资源
    最近更新 更多