2009-07-26 3 views
32

Мне нужно, чтобы некоторые свойства были уникальными. Как я могу это достичь?Как определить уникальное свойство для модели в Google App Engine?

Есть что-то вроде unique=True?

Я использую Google App Engine для Python.

+0

Blixt, редактирование, чтобы заставить название сказать, что движок приложения хорош, поскольку многие вопросы с движком приложений являются агностиками языка, но где-то в этом вопросе стоит упомянуть, что плакат заинтересован в ответе на python, а не в java. –

+0

Вы правы. Я также подумал над добавлением его, но решил, что решение Java также укажет OP в правильном направлении. Но я полагаю, что версии GAE на Java и Python достаточно разные, чтобы этот вопрос был специфичным для Python. – Blixt

ответ

21

Нет встроенного ограничения для обеспечения уникальности значения. Вы можете сделать это, однако:

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 для извлечения одного или нескольких из этих составных ключевых имен ключей, и если вы получите одно или несколько значений не None, вы знаете, что есть повторяющиеся значения (и проверка которых не была None, вы будете знать, какие из них не были уникальный.)


Как onebyone упоминается в комментариях, эти подходы – путем их получения первых, положить позже природы – запустить вопросы параллелизм риск. Теоретически, объект может быть создан сразу после проверки существующего значения, а затем код после проверки будет выполняться, что приведет к дублированию значений. Чтобы предотвратить это, вы должны использовать транзакции: 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(...) Предположим, что каждая пара ключ/значение должно быть уникальным для возврата успеха. Транзакция будет использовать единую группу объектов для каждого типа модели. Таким образом, транзакция является надежной для нескольких разных полей одновременно (только для одного поля это будет намного проще). Кроме того, даже если у вас есть поля с тем же именем в одной или нескольких моделях, они не будут конфликтовать с друг друга.

+0

Их можно поместить в транзакции, но я оставлю это в OP. – Blixt

+0

@onebyone: Я обновил сообщение с кодом, чтобы показать, что я имел в виду со вторым методом. Он использует очень широкие группы сущностей, но я бы надеялся, что он не будет делать больше пяти вставок 'put's per second per model. – Blixt

+0

Насколько я понимаю, это создает группу сущностей для каждого класса, ограничивая скорость вставки в секунду в секунду? Предложит ли он какую-либо несогласованность сделать хэш всех проверяемых свойств и добавить их как строку к «уникальным значениям», тем самым создав несколько групп сущностей? – petr

24

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_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 Ключевые аргументы перейти к конструктору класса модели, если экземпляр с указанным именем ключа не существует. Родительский аргумент требуется, если желаемый объект имеет родительский элемент.

Примечание: get_or_insert() не принимает объект RPC.

Метод возвращает экземпляр класса модели, который представляет запрошенный объект, независимо от того, существовал он или был создан методом. Как и во всех операциях хранилища данных, этот метод может вызвать TransactionFailedError, если транзакция не может быть выполнена.