在NDB中存储关系值的有效方法


问题内容

我有这个数据模型(我做到了,所以,如果有更好的方法,请告诉我)。从基本上讲,我Club可以有很多Courses。现在我想知道一个俱乐部的全部membersinstructorsmembersinstructors存储在Course模型中,并Club具有对它们的引用。参见代码。

class Course(ndb.Model):
    ...
    instructor_keys = ndb.KeyProperty(kind="User", repeated=True)
    member_keys = ndb.KeyProperty(kind="User", repeated=True)

    @property
    def instructors(self):
        return ndb.get_multi(self.instructor_keys)

    @property
    def members(self):
        return filter(lambda x: x.is_active, ndb.get_multi(self.member_keys))

    def add_instructor(self, instructor):
        if instructor.key not in self.instructor_keys:
            self.instructor_keys.append(instructor.key)
            self.put()

    def rm_instructor(self, instructor):
        if instructor.key in self.instructor_keys:
            self.instructor_keys.remove(instructor.key)
            self.put()

    def add_member(self, member):
        if member.key not in self.member_keys:
            self.member_keys.append(member.key)
            self.put()

    def rm_member(self, member):
        if member.key in self.member_keys:
            self.member_keys.remove(member.key)
            self.put()

class Club(ndb.Model):
    ...
    owners_keys = ndb.KeyProperty(kind="User", repeated=True)
    course_keys = ndb.KeyProperty(kind="Course", repeated=True)


    @property
    def members(self):
        # TODO: is this correct? efficient?
        l_members = []
        for courses in self.courses:
            l_members = l_members + courses.members
        return l_members

    @property
    def trainers(self):
        l_trainers = []
        for courses in self.courses:
            l_trainers = l_trainers + courses.trainers
        return l_trainers

    @property
    def owners(self):
        return ndb.get_multi(self.owners_keys)

    @property
    def courses(self):
        return filter(lambda x: x.is_active, ndb.get_multi(self.course_keys))

    def add_owner(self, owner):
        if owner.key not in self.owners_keys:
            self.owner_keys.append(owner.key)
            self.put()

    def rm_owner(self, owner):
        if owner.key in self.owners_keys:
            self.owner_keys.remove(owner.key)
            self.put()

    def add_course(self, course):
        if course.key not in self.courses_keys:
            self.course_keys.append(course.key)
            self.put()

    def rm_course(self, course):
        if course.key in self.courses_keys:
            self.course_keys.remove(course.key)
            self.put()

    def membership_type(self, user):
        if user.key in self.owners_keys:
            return "OWNER"
        elif user in self.members:
            return "MEMBER"
        elif user in self.trainers:
            return "TRAINER"
        else:
            raise Exception("The user %s is not in the club %s" % (user.id, self.id))

现在,@propertyCourse似乎是确定了我。(我是对的吗?)但Club似乎效率很低。每次我必须遍历所有Courses以计算membersand时trainers。另外,这些方法不会被缓存,而是每次都会计算。因此,如果它非常昂贵。

有什么建议吗?我当时正在考虑在中添加instructorsmembers作为键的列表Club,并在每次将某人添加到时更新俱乐部Course,但不确定它是否正确。

PS:是否有更好的方法也可以filter进行ndb.get_multi


问题答案:

我会尝试规范化您的模型,而不是使用非规范化模型:

class CourseInscription(ndb.Model):
    member = ndb.KeyProperty(kind='User', required=True)
    course = ndb.KeyProperty(kind='Course', required=True)
    is_active = ndb.BooleanProperty(default=True)

然后,您可以添加类似

class Course(ndb.Model):
    # all the stuff already there

    @property
    def members(self):
        return CourseInscription.query(CourseInscription.course == self.key)

通常,我更喜欢只返回查询,让调用者决定直接调用它,或者添加更多过滤/排序,而不是直接执行 ndb.get_multi

我通常做的另一种不错的做法是使用它们的父级为关系实体构造id,因此我可以使用get by id轻松检查是否存在,而不必查询

class CourseInscription(ndb.Model):
    # all the other stuff

    @staticmethod
    def build_id(user_key, course_key):
        return '%s/%s' % (user_key.urlsafe(), course_key.urlsafe())

# somewhere I can create an inscription like

CourseInscription(
    id=CourseInscription.build_id(user_key, course_key),
    user=user_key, course=course_key, is_active=True
).put()

# somewhere else I can check if a User is in a Course by just getting... no queries needed
if ndb.Key(CourseInscription, CourseInscription.build_id(user, course)).get():
    # Then the user is in the course!
else:
    # then it's not