2013-08-19 4 views
11

Модель NDB содержит два свойства: email и password. Как избежать добавления в базу данных двух записей с тем же email? NDB не имеет опции UNIQUE для свойства, как это делают реляционные базы данных.Сохранение уникальности свойства в базе данных NDB

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

Я не уверен, что здесь могут помочь транзакции, я испытываю такое впечатление после прочтения некоторых руководств. Возможно, синхронных транзакций? Означает ли это одно за раз?

ответ

6

Создайте ключ объекта по электронной почте, затем используйте get_or_insert, чтобы проверить, существует ли.

Also read about keys , entities. и models

#ADD 
key_a = ndb.Key(Person, email); 
person = Person(key=key_a) 
person.put() 

#Insert unique  
a = Person.get_or_insert(email) 

или если вы хотите просто проверить

#ADD 
key_a = ndb.Key(Person, email); 
person = Person(key=key_a) 
person.put() 

#Check if it's added 
new_key_a =ndb.Key(Person, email); 
a = new_key_a.get() 
if a is not None: 
    return 

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

Для этого возможно, вам нужно сохранить электронное письмо в другом объекте и предоставить пользователю родительский элемент.

Другой способ - использовать транзакции и проверить свойство электронной почты. Работа транзакции в пути: во-первых, это первая победа. Концепция, которая означает, что если 2 пользователя проверяют электронную почту, только первая (удачливая) будет успешной, поэтому ваши данные будут согласованы.

+0

Использование get_or_insert() Я не буду уверен: либо я добавил новую запись, либо у меня есть существующая. – Graduate

+1

изменение электронной почты будет болью в ** ... или лучше .. невозможно :) Так что для писем это нехорошее решение. – Lipis

+0

@ Graduate yes должно быть 'key_a' исправлено это. –

4

Возможно, вы ищете модуль аутентификации webapp2, который может справиться с этим для вас. Его можно импортировать следующим образом: import webapp2_extras.appengine.auth.models. Посмотрите here на полный пример.

2

Я также столкнулся с этой проблемой, и решение выше не решить мою проблему:

  • , что делает его одним из ключевых является неприемлемым в моем случае (мне нужно свойство быть изменяемым в будущем)
  • Использование транзакций в свойстве электронной почты не работает AFAIK (вы не можете делать запросы о неключевых именах внутри транзакций, поэтому вы не можете проверить, существует ли электронное письмо).

В результате я создал отдельную модель без свойств и уникальное свойство (адрес электронной почты) в качестве имени ключа. В основной модели я храню ссылку на модель электронной почты (вместо сохранения электронной почты в виде строки). Затем я могу сделать «change_email» транзакцию, которая проверяет уникальность, просматривая электронную почту по ключу.

0

Это то, с чем я столкнулся, и я остановился на вариации решения @ Ремко. Моя основная проблема с проверкой существующего объекта с данным адресом электронной почты - это потенциальное состояние гонки, как указано. Я добавил отдельную модель, которая использует адрес электронной почты в качестве ключа и имеет свойство, содержащее токен. Используя get_or_insert, маркер возвращаемых объектов может быть проверен на переданный токен, и если они совпадают, тогда была вставлена ​​модель.

import os 
from google.appengine.ext import ndb 

class UniqueEmail(ndb.Model): 
    token = ndb.StringProperty() 

class User(ndb.Model): 
    email = ndb.KeyProperty(kind=UniqueEmail, required=True) 
    password = ndb.StringProperty(required=True) 

def create_user(email, password): 
    token = os.urandom(24) 
    unique_email = UniqueEmail.get_or_insert(email, 
              token=token) 

    if token == unique_email.token: 
     # If the tokens match, that means a UniqueEmail entity 
     # was inserted by this process. 
     # Code to create User goes here. 
    # The tokens do not match, therefore the UniqueEmail entity 
    # was retrieved, so the email is already in use. 
    raise ValueError('That user already exists.')