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
Ответы
Ответ 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
, чтобы получить эти значения.