固有のプロパティを制御するための一般的な構造を実装しました。このソリューションは、いくつかの種類とプロパティに使用できます。さらに、このソリューションは他の開発者に対して透過的であり、通常どおり 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)