0

Я пытался создать объекты, обладающие свойством, которое должно быть уникальным или None что-то подобное:ОПРС VERIFY сущности уникальность в транзакции

class Thing(ndb.Model): 
    something = ndb.StringProperty() 
    unique_value = ndb.StringProperty() 

Поскольку приводная радиостанция не имеет возможности указать, что свойство должно быть уникальным это только естественно, что я делаю это вручную, как это:

def validate_unique(the_thing): 
    if the_thing.unique_value and Thing.query(Thing.unique_value == the_thing.unique_value).get(): 
     raise NotUniqueException 

Это работает как шарм, пока я не хочу, чтобы это сделать в сделке NDB, который я использую для создания/обновления объектов. Как:

@ndb.transactional 
def create(the_thing): 
    validate_unique(the_thing) 
    the_thing.put() 

Однако ОПРС кажется только разрешить запросы по предку, проблема моя модель не имеет предка/родителя. Я мог бы сделать следующее, чтобы предотвратить эту ошибку от выскакивает:

@ndb.non_transactional 
def validate_unique(the_thing): 
    ... 

Это чувствует себя немного неуместно, объявив что-то, чтобы быть сделки, а затем с один (важно) частью делаются за пределами сделки. Я хотел бы знать, если это путь, или если есть (лучше) альтернатива.

Также некоторое объяснение относительно того, почему ndb разрешает запросы предков, было бы неплохо.

+1

Потому что это то, что происходит в хранилище данных. Вам нужно перестать думать о хранилище данных как реляционной базы данных; это не так, и вы можете получить только согласованные данные. –

+1

Какое уникальное значение вы хотите сохранить? Если вы присвоите этому уникальному значению имя ключа, тогда легко убедиться в уникальности, поскольку имена ключей должны быть уникальными. –

ответ

1

Поскольку ваша проверка на уникальность включает в себя (глобальный) запрос, это означает, что она подчиняется datastore's eventual consistency, то есть она не будет работать, поскольку запрос может не обнаруживать только что созданные объекты.

Одним из вариантов было бы переключиться на запрос предка, если ваше ожидаемое использование позволяет использовать такую ​​архитектуру данных (или какой-либо другой строго согласованный метод) - более подробно в той же статье.

Другим вариантом является использование дополнительной части данных в качестве временного кеша, в которой вы бы сохранили список всех вновь созданных сущностей для «некоторое время» (давая им достаточно времени, чтобы стать видимыми в глобальном запросе) который вы проверили бы в validate_unique() в дополнение к результатам запроса. Это позволит вам сделать запрос за пределами транзакции и только ввести транзакцию, если уникальность по-прежнему возможна, но конечным результатом является ручная проверка кеша внутри транзакции (т. Е. Нет запроса внутри транзакции).

Существует 3-й вариант (с некоторым дополнительным потреблением памяти в качестве цены), основанный на применении уникальных идентификаторов объектов хранилища данных для определенной модели сущности с одним и тем же родителем (или вообще без родителя). Вы могли бы иметь модель, как это:

class Unique(ndb.Model): # will use the unique values as specified entity IDs! 
    something = ndb.BooleanProperty(default=False) 

, который вы будете использовать, как это (в данном примере используется уникальный ключ родителя, который позволяет повторно использовать модели для нескольких свойств с уникальными значениями, вы можете отказаться от родителей если это вам не нужно):

@ndb.transactional 
def create(the_thing): 
    if the_thing.unique_value: 
     parent_key = get_unique_parent_key() 
     exists = Unique.get_by_id(the_thing.unique_value, parent=parent_key) 
     if exists: 
      raise NotUniqueException 
     Unique(id=the_thing.unique_value, parent=parent_key).put() 
    the_thing.put() 


def get_unique_parent_key(): 

    parent_id = 'the_thing_unique_value' 
    parent_key = memcache.get(parent_id) 
    if not parent_key: 
     parent = Unique.get_by_id(parent_id) 
     if not parent: 
      parent = Unique(id=parent_id) 
      parent.put() 
     parent_key = parent.key 
     memcache.set(parent_id, parent_key) 
    return parent_key 
+0

Это очень похоже на решение, которое я принял. Спасибо, что опубликовали такое подробное объяснение! – Mathijs

Смежные вопросы