Подтвердить что ты не робот

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

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

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

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

4b9b3361

Ответ 1

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

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


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

Ответ 2

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.

Получаемые и последующие (возможные) вкладыши завернуты в транзакцию для обеспечения атомарности. Ths означает, что 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, если транзакция не может быть завершена.