3

I would like to create movies database, where user will be able to mark movies he/she watched and liked:

class Movies(ndb.Model):
    watched = ndb.UserProperty()
    liked = ndb.UserProperty()

Will that work? I use Google accounts. How should I choose later all movies user liked?


Upd. I've followed systempuntoout approach and use the following code to save user choices:

user = users.get_current_user()
if user:
    userschoices = models.UsersChoices(
        movie=ndb.Key(models.Movies, movie_id), # TODO: what if movie_id is wrong?
        watched=True,
        user_id=user.user_id()
        )
    try:
        userschoices.put()
        self.response.out.write('1')
    except:
        self.response.out.write('0')

But if user makes his choice several times, then several records are added to the datastore... Wouldn't be it better just to save user id and movie id as keyname?

userschoices = models.UsersChoices.get_by_id(user.user_id() + '-' + movie_id)
if userschoices is None:
    userschoices = models.UsersChoices(id=user.user_id() + '-' + movie_id)
userschoices.movie = ndb.Key(models.Movies, movie_id) # TODO: what if movie_id is wrong?
userschoices.user_id = user.user_id()
if option == 'liked':
    userschoices.liked = True
elif option == 'watched':
    userschoices.watched = True

However, with such approach if I don't pass liked, then it overwrites its value with None (the same with watched, if not passed, None is used).

4

2 回答 2

8

我会使用两种不同的模型,一种存储所有Movies详细信息,另一种存储UserChoices

class Movies(ndb.Model):
    title = ndb.StringProperty(required=True)
    director = ndb.StringProperty()
    whatever = ndb.StringProperty()

class UsersChoices(ndb.Model):
    movie = ndb.KeyProperty(kind=Movies, required=True)
    watched = ndb.BooleanProperty(required=True)
    liked = ndb.BooleanProperty(required=True)
    user_id = ndb.StringProperty(required=True)

    @classmethod
    def get_liked_movies(cls, user_id):
        return cls.query(cls.user_id == user_id, cls.liked == true).fetch(10)

    @classmethod
    def get_watched_movies(cls, user_id):
        return cls.query(cls.user_id == user_id, cls.watched == true).fetch(10)

    @classmethod
    def get_by(cls, user_id, movie_key):
        return cls.query(cls.user_id == user_id, cls.movie == movie_key).get()

如果您需要存储有关用户的信息,您应该创建您的模型,由用户 APIUserInfo键入,并包含您的应用程序所需的所有详细信息属性。user_id

class UserInfo(ndb.Model):
        #Keyed by user_id 
        nickname = ndb.StringProperty()
        email = ndb.StringProperty()

要创建一个新的UserInfo,你可以这样做:

from google.appengine.api import users

user = users.get_current_user()
userinfo = UserInfo(
        id = user.user_id(),
        nickname = user.keyname(),
        email = user.email()
      )
userinfo.put()

然后,当用户登录时,使用他/她user_id来检索观看/喜欢的电影。

from google.appengine.api import users

user = users.get_current_user()
userinfo = ndb.Key(UserInfo, user.user_id()).get()
watched_movies = UsersChoices.get_watched_movies(userinfo.key.id())
liked_movies = UsersChoices.get_liked_movies(userinfo.key.id())
于 2012-07-30T15:43:18.743 回答
3

看来您正在尝试建模多对多关系。 有几种方法可以对这种关系进行建模(参见多对多部分)。另见尼克的博客。(不幸的是,这些引用都不是为 NDB 编写的,因此,例如,您不能使用collection_name,即反向引用。但它们仍然有助于向您展示如何将数据分解为不同的模型。)

这是您可以使用“连接表”/“关系模型”的一种方法:

class Movie(ndb.Model):
    title = ndb.StringProperty(required=True)

class LikedMovie(ndb.Model):
    movie = ndb.KeyProperty(kind=Movie, required=True)
    user  = ndb.StringProperty(required=True)  # user.user_id()

class WatchedMovie(ndb.Model):
    movie = ndb.KeyProperty(kind=Movie, required=True)
    user  = ndb.StringProperty(required=True)  # user.user_id()

...
    movies_user_likes = LikedMovie.query(LikedMovie.user == user.user_id()).fetch()

根据您的应用程序将支持多少用户以及数据库更新的频率,使用重复属性(即用户列表)而不是连接表可能更有效:

class Movie(ndb.Model):
    title = ndb.StringProperty(required=True)
    users_who_watched = ndb.StringProperty(repeated=True)  # list of user.user_id()s
    users_who_liked   = ndb.StringProperty(repeated=True)  # list of user.user_id()s

...
    movies_user_likes = Movie.query(Movie.users_who_liked == user.user_id()).fetch(projection=[Movie.title])

请注意,我在上面使用了投影查询,因此 users_who_watched 列表不会与查询结果一起返回。您可能不需要这些,这应该会显着加快获取速度。

例如,如果您预计观看或喜欢某部电影的用户少于 1,000 人,那么列表方法可能会更好。

如需更高级的技术,请参阅在 App Engine 上构建可扩展的复杂应用程序,其中 Brett 展示了如何使用父键将重复/列表属性移动到单独的模型中。

于 2012-07-30T16:20:19.733 回答