12

NDB モデルにはemail、 との 2 つのプロパティが含まれていますpassword。データベースに同じ 2 つのレコードを追加しないようにするにはどうすればよいemailですか? リレーショナル データベースのように、NDB にはプロパティの UNIQUE オプションがありません。

email追加する前にnew がデータベースにないことを確認することは、私には満足できません。なぜなら、2 つの並列プロセスが同時に確認を行い、それぞれが同じemail.

ここでトランザクションが役立つかどうかはわかりません。いくつかのマニュアルを読んだ後、この印象を受けました。多分同期トランザクション?一人ずつということですか?

4

5 に答える 5

6

電子メールでエンティティのキ​​ーを作成し、get_or_insertを使用して存在するかどうかを確認します。

キー、エンティティについてもお読みください。モデル

#ADD
key_a = ndb.Key(Person, email);
person = Person(key=key_a)
person.put()

#Insert unique    
a = Person.get_or_insert(email)

または、単に確認したい場合

#ADD
key_a = ndb.Key(Person, email);
person = Person(key=key_a)
person.put()

#Check if it's added
new_key_a =ndb.Key(Person, email);
a = new_key_a.get()
if a is not None:
    return

気をつけて。電子メールの変更は非常に困難です (新しいエントリを作成し、すべてのエントリを新しい親にコピーする必要があります)。

そのためには、メールを別のエンティティに保存し、ユーザーをその親にする必要があるかもしれません。

もう 1 つの方法は、Transactions を使用して email プロパティを確認することです。途中でのトランザクションの作業: 最初にコミットしたものが最初に勝ちます。2 人のユーザーがメールをチェックした場合、最初の (幸運な) 人だけが成功するため、データの一貫性が保たれるという概念。

于 2013-08-19T12:14:36.600 に答える
2

私もこの問題に遭遇しましたが、上記の解決策では問題が解決しませんでした:

  • 私の場合、それをキーにすることは受け入れられませんでした(将来、プロパティを変更可能にする必要があります)
  • 電子メール プロパティでトランザクションを使用しても機能しません (トランザクション内で非キー名に対してクエリを実行できないため、電子メールが既に存在するかどうかを確認できません)。

結局、プロパティのない別のモデルを作成し、一意のプロパティ (電子メール アドレス) をキー名として作成しました。メイン モデルでは、メール モデルへの参照を保存します (メールを文字列として保存するのではなく)。次に、「change_email」をキーでメールを検索して一意性をチェックするトランザクションにすることができます。

于 2015-07-27T14:22:35.313 に答える
0

固有のプロパティを制御するための一般的な構造を実装しました。このソリューションは、いくつかの種類とプロパティに使用できます。さらに、このソリューションは他の開発者に対して透過的であり、通常どおり NDB メソッドの put および delete を使用します。

1) Kind UniqueCategory: 情報をグループ化するための一意のプロパティのリスト。例:

‘User.nickname’

2) Kind Unique: 各一意のプロパティの値が含まれます。キーは、制御したい独自のプロパティ値です。key または key.id() の代わりにメインエンティティの urlsafe を保存します。これは、より実用的であり、親に問題がなく、さまざまな種類に使用できるためです。例:

parent: User.nickname
key: AVILLA
reference_urlsafe: ahdkZXZ-c3RhcnQtb3BlcmF0aW9uLWRldnINCxIEVXNlciIDMTIzDA (User key)

3) 親切なユーザー: たとえば、電子メールとニックネームの一意の値を制御したい。ユニークな特性を持つ「ユニークネス」というリストを作成しました。トランザクショナル モードでメソッド put を上書きし、1 つのエンティティが削除されたときのフック _post_delete_hook を記述しました。

4) Exception ENotUniqueException: 一部の値が重複している場合に発生するカスタム例外クラス。

5) プロシージャ check_uniqueness: 値が重複しているかどうかをチェックします。

6) 手順 delete_uniqueness: 主エンティティが削除されたときに一意の値を削除します。

ヒントや改善点は大歓迎です。


class UniqueCategory(ndb.Model):
    # Key = [kind name].[property name]

class Unique(ndb.Model):
    # Parent = UniqueCategory
    # Key = property value
    reference_urlsafe = ndb.StringProperty(required=True)

class ENotUniqueException(Exception):
    def __init__(self, property_name):
        super(ENotUniqueException, self).__init__('Property value {0} is duplicated'.format(property_name))

        self. property_name = property_name

class User(ndb.Model):
    # Key = Firebase UUID or automatically generated
    firstName = ndb.StringProperty(required=True)
    surname = ndb.StringProperty(required=True)
    nickname = ndb.StringProperty(required=True)
    email = ndb.StringProperty(required=True)

    @ndb.transactional(xg=True)
    def put(self):
        result = super(User, self).put()
        check_uniqueness (self)
        return result

    @classmethod
    def _post_delete_hook(cls, key, future):
        delete_uniqueness(key)

    uniqueness = [nickname, email]

def check_uniqueness(entity):
    def get_or_insert_unique_category(qualified_name):
        unique_category_key = ndb.Key(UniqueCategory, qualified_name)
        unique_category = unique_category_key.get()
        if not unique_category:
           unique_category = UniqueCategory(id=qualified_name)
           unique_category.put()

        return unique_category_key

    def del_old_value(key, attribute_name, unique_category_key):
        old_entity = key.get()
        if old_entity:
           old_value = getattr(old_entity, attribute_name)
           if old_value != new_value:
               unique_key = ndb.Key(Unique, old_value, parent=unique_category_key)
               unique_key.delete()

    # Main flow
    for unique_attribute in entity.uniqueness:
        attribute_name = unique_attribute._name
        qualified_name = type(entity).__name__ + '.' + attribute_name
        new_value = getattr(entity, attribute_name)

        unique_category_key = get_or_insert_unique_category(qualified_name)
        del_old_value(entity.key, attribute_name, unique_category_key)

        unique = ndb.Key(Unique, new_value, parent=unique_category_key).get()

        if unique is not None and unique.reference_urlsafe != entity.key.urlsafe():
           raise ENotUniqueException(attribute_name)
        else:
           unique = Unique(parent=unique_category_key,
                           id=new_value, 
                           reference_urlsafe=entity.key.urlsafe())
           unique.put()

def delete_uniqueness(key):
    list_of_keys = Unique.query(Unique.reference_urlsafe == key.urlsafe()).fetch(keys_only=True)

    if list_of_keys:
        ndb.delete_multi(list_of_keys)
于 2018-04-05T11:53:59.033 に答える