Как определить уникальное свойство для модели в Google App Engine?

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

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

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

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


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


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

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

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