Как я могу создать два уникальных, запрашиваемых поля для модели данных хранилища данных GAE? - PullRequest
0 голосов
/ 05 июля 2011

Сначала немного настройки.На прошлой неделе у меня были проблемы с реализацией определенной методологии, которую я создал, которая позволила бы мне управлять двумя уникальными полями, связанными с одним объектом db.Model.Поскольку это невозможно, я создал родительский класс сущностей и дочерний класс сущностей, каждому из которых присвоено одно из уникальных значений.Вы можете найти мой предыдущий вопрос , расположенный здесь , который включает в себя мой пример кода и общее объяснение моего процесса вставки.

На мой исходный вопрос кто-то заметил, что мое решение не решит мою проблему необходимости двух уникальных полей, связанных с одним объектом db.Model.

Моя реализация пыталась решить эту проблему путем реализацииСтатический метод, который создает ParentEntity и его свойство key_name, присваивается одному из моих уникальных значений.На втором этапе моего процесса я создаю дочернюю сущность и назначаю родительскую сущность родительскому параметру.Оба эти шага выполняются в транзакции БД, поэтому я предположил, что это заставит работать ограничение уникальности, так как оба моих значения были сохранены в двух отдельных полях key_name в двух разных моделях.

Указал комментаторчто это решение не будет работать, потому что, когда вы устанавливаете родительский объект для дочерней сущности, имя ключа больше не является уникальным для всей модели, а вместо этого уникальным для записей родительских и дочерних объектов.Облом ...

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

Сначала я создаю родительский объект, как упомянуто выше.Затем я создаю дочернюю сущность и присваиваю свое второе уникальное значение ее key_name.Разница в том, что вторая сущность имеет свойство ссылки на родительскую модель.Моя первая сущность назначается ссылочному свойству, но не родительскому параметру.Это не навязывает ссылку «один к одному», но сохраняет оба моих значения уникальными, и я могу управлять природой этих объектов, пока я могу контролировать процесс вставки из транзакции.

Это новое решение все еще проблематично.Согласно документации GAE Datastore, вы не можете выполнить несколько обновлений БД в одной транзакции, если различные объекты в обновлении не принадлежат к одной и той же группе объектов.Поскольку я больше не делаю свою первую сущность родителем второй, они больше не являются частью одной и той же группы сущностей и не могут быть вставлены в одну и ту же транзакцию.

Я вернулся к исходной точке.Что я могу сделать, чтобы решить эту проблему?В частности, что я могу сделать для обеспечения двух уникальных значений, связанных с одной сущностью модели.Как вы можете видеть, я хочу стать немного креативнее.Можно ли это сделать?Я знаю, что это будет связано с готовым решением, но должен быть способ.

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

class ParentEntity(db.Model):
    str1_key =  db.StringProperty()
    str2 =      db.StringProperty()

    @staticmethod
    def InsertData(string1, string2, string3):
        try:
            def txn():
                #create first entity
                prt = ParentEntity(
                    key_name=string1, 
                    str1_key=string1, 
                    str2=string2)
                prt.put()

                #create User Account Entity
                    child = ChildEntity(
                    key_name=string2, 
                    #parent=prt, #My prt object was previously the parent of child
                    parentEnt=prt,
                    str1=string1, 
                    str2_key=string2,
                    str3=string3,)
                child.put()
                return child
            #This should give me an error, b/c these two entities are no longer in the same entity group. :(
            db.run_in_transaction(txn)
        except Exception, e:
            raise e

class ChildEntity(db.Model):
    #foreign and primary key values
    str1 =      db.StringProperty()
    str2_key =  db.StringProperty()

    #This is no longer a "parent" but a reference
    parentEnt = db.ReferenceProperty(reference_class=ParentEntity)
    #pertinent data below
    str3 =      db.StringProperty()

Ответы [ 2 ]

1 голос
/ 06 июля 2011

Система, которую вы описываете, будет работать за счет транзакционности. Обратите внимание, что вторая сущность больше не является дочерней сущностью - это просто другая сущность со свойством ReferenceProperty.

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

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

0 голосов
/ 06 июля 2011

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

Приведенный ниже код является дополнительной модификацией кода в моем вопросе.В частности, я создал еще один класс Model с именем EGEnforcer (что означает Entity Group Enforcer.)

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

Для этого ясоздайте запись EGEnforcer при первом запуске приложения.Затем, когда возникает необходимость сделать новую запись в моих моделях, я запрашиваю у EGEnforcer запись, связанную с моими парными моделями.После того, как я получаю свою запись EGEnforcer , я делаю ее родителем обеих записей.Виола!Теперь все мои данные связаны с одной и той же группой сущностей.

Поскольку параметр * key_name * уникален только для групп parent-key_name, это должно информировать мои ограничения уникальности, поскольку все мои FirstEntity (ранее ParentEntity ) записи будут иметь одного и того же родителя.Аналогично, мое SecondEntity (ранее ChildEntity ) также должно иметь уникальное значение, сохраняемое как имя_ключа, поскольку родительский элемент также всегда одинаков.

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

#My new class containing unique entries for each pair of models associated within one another.
class EGEnforcer(db.Model): 
KEY_NAME_EXAMPLE = 'arbitrary unique value'

    @staticmethod
    setup():
        ''' This only needs to be called once for the lifetime of the application. setup() inserts a record into EGEnforcer that will be used as a parent for FirstEntity and SecondEntity entries.  '''
        ege = EGEnforcer.get_or_insert(EGEnforcer.KEY_NAME_EXAMPLE)
    return ege

class FirstEntity(db.Model):
    str1_key =  db.StringProperty()
    str2 =      db.StringProperty()

    @staticmethod
    def InsertData(string1, string2, string3):
        try:
            def txn():
                ege = EGEnforcer.get_by_key_name(EGEnforcer.KEY_NAME_EXAMPLE)
                prt = FirstEntity(
                    key_name=string1, 
                    parent=ege) #Our EGEnforcer record.
                prt.put()

                child = SecondEntity(
                    key_name=string2, 
                    parent=ege, #Our EGEnforcer record.
                    parentEnt=prt,
                    str1=string1, 
                    str2_key=string2,
                    str3=string3)
                child.put()
                return child
        #This works because our entities are now part of the same entity group
            db.run_in_transaction(txn)
        except Exception, e:
            raise e

class SecondEntity(db.Model):
    #foreign and primary key values
    str1 =      db.StringProperty()
    str2_key =  db.StringProperty()

    #This is no longer a "parent" but a reference
    parentEnt = db.ReferenceProperty(reference_class=ParentEntity)

#Other data...
    str3 =      db.StringProperty()

Одна быстрая заметка - Ник Джонсон определил мою потребность в этом решении:

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

Это именно то, что мне нужно, но мое решение, очевидно, немного отличается от вашего предложения.Мой метод позволяет транзакции полностью произойти или полностью провалиться.В частности, когда пользователь создает учетную запись, он сначала входит в свою учетную запись Google.Затем они вынуждены перейти на страницу создания учетной записи, если нет записи, связанной с их учетной записью Google в SecondEntity (что на самом деле UserAccount формирует мой фактический сценарий.) Если процесс вставки завершается неудачноони перенаправляются на страницу создания с указанием причины этой ошибки.

Это может быть связано с тем, что их идентификатор не является уникальным или, возможно, является тайм-аутом транзакции.Если при добавлении новой учетной записи пользователя истечет время ожидания, я захочу узнать об этом, но в ближайшем будущем я реализую некоторую форму проверки и баланса.Сейчас я просто хочу начать работу, но это ограничение уникальности является абсолютной необходимостью.

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

...