Ndb моделирование one-to-many: преимущества повторного KeyProperty и внешнего ключа - программирование

Ndb моделирование one-to-many: преимущества повторного KeyProperty и внешнего ключа

Мой вопрос о моделировании отношений один-ко-многим в ndb. Я понимаю, что это может быть сделано (по крайней мере) двумя разными способами: с повторным свойством или с "внешним ключом". Я создал небольшой пример ниже. В основном у нас есть статья, которая может иметь произвольное количество тегов. Предположим, что тег можно удалить, но не может быть изменен после его добавления. Предположим также, что мы не беспокоимся о безопасности транзакций.

Мой вопрос: какой предпочтительный способ моделирования этих отношений?

Мои соображения:

  • Подход (A) требует двух записей для каждого тега, который добавляется к статья (одна для статьи и одна для тега), тогда как подход (B) требуется только одна запись (только тег).
  • Подход (A) использует ndb при извлечении всех тегов для статьи, тогда как в случае подхода (B) требуется запрос (и, кроме того, некоторые пользовательское кэширование)

Есть ли какие-то вещи, которые мне не хватает здесь, любые другие соображения, которые следует учитывать?

Большое спасибо за вашу помощь.

Пример (A):

class Article(ndb.Model):
    title = ndb.StringProperty()
    # some more properties
    tags = ndb.KeyProperty(kind="Tag", repeated=True)

    def create_tag(self):
        # requires two writes
        tag = Tag(name="my_tag")
        tag.put()
        self.tags.append(tag)
        self.put()

    def get_tags(self):
        return ndb.get_multi(self.tags)

class Tag(ndb.Model):
    name = ndb.StringProperty()
    user = ndb.KeyProperty(Kind="User") #  User that created the tag
    # some more properties

Пример (В):

class Article(ndb.Model):
    title = ndb.StringProperty()
    # some more properties

    def create_tag(self):
        # requires one write
        tag = Tag(name="my_tag", article=self.key)
        tag.put()

    def get_tags(self):
        # obviously we could cache this query in memcache
        return Tag.gql("WHERE article :1", self.key)

class Tag(ndb.Model):
    name = ndb.StringProperty()
    article = ndb.KeyProperty(kind="Article")
    user = ndb.KeyProperty(Kind="User") #  User that created the tag
    # some more properties
4b9b3361

Ответ 1

Обсудите ли вы следующее: Structured Properties https://developers.google.com/appengine/docs/python/ndb/properties#structured. Краткая дискуссия о Contact и Addresse может упростить вашу проблему. Также посмотрите https://developers.google.com/appengine/docs/python/ndb/queries#filtering_structured_properties. Обсуждения очень короткие.

Кроме того, глядя на то, что объединения не разрешены, опция A выглядит лучше.

Ответ 2

Как указано ранее, в Datastore нет объединений, поэтому все понятия "Foreign Key" не применяются. Что можно сделать, так это использовать класс Query для запроса вашего хранилища данных для правильного тега.

Например, если вы используете конечные точки, то:

class Tag(ndb.model):
    user = ndb.UserProperty()

И во время запроса выполните:

query.filter(Tag.user == endpoints.get_current_user())

Ответ 3

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

Как вы упомянули, выбор тегов по их ключам происходит намного быстрее, чем выполнение запроса. Кроме того, если вам нужно только имя тега и пользователь, вы можете создать тег с User в качестве родительского ключа и Name в качестве идентификатора тега:

User -> Name -> Tag

Чтобы создать этот тег, вы должны использовать:

tag = Tag(id=name, parent=user, ...)
article.tags.push(tag)
ndb.put_multi([tag, article])

Затем, когда вы извлекаете теги,

for tag in article.tags:
    user = tag.parent()
    name = tag.id()

Затем каждый ключ, сохраненный в Article.tags, будет содержать ключ пользователя и имя Tag! Это избавит вас от чтения в Tag, чтобы получить эти значения.