【问题标题】:Efficient way to store relation values in NDB在 NDB 中存储关系值的有效方法
【发布时间】:2014-12-31 15:49:43
【问题描述】:

我有这个数据模型(我做了,如果有更好的方法,请告诉我)。 Baically 我有 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))

现在,Course 上的 @property 对我来说似乎没问题。 (我对吗?) 但是Club 中的那个似乎效率很低。每次我都要迭代所有Courses 来计算memberstrainers。此外,这些方法不会被缓存,而是每次都计算。因此,如果它非常昂贵。

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

PS:有没有更好的方法可以在ndb.get_multi 上同时添加filter

【问题讨论】:

    标签: python google-app-engine google-cloud-datastore app-engine-ndb


    【解决方案1】:

    我会尝试规范化你的模型,而不是去规范化的模型:

    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,因此我可以通过 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
    

    【讨论】:

    • 谢谢。我不确定在 NDB 中使用规范化是否是正确的方法。我同意你的观点,我应该返回查询。但是self.member_keys 是一个键列表,在这种情况下我应该返回什么?
    • def build_id(user_key, course_key): 也会自动将关系的 id 设置为该值?或者它是如何在看跌期权上运作的?
    • 我修复了 build_id 示例以显示它在创建时使用(在 Inscription 类中,而不是在 Course 中)
    • 除了味道...这是 Guido 关于使用重复属性和 ~1000 限制的回复:stackoverflow.com/questions/15377119/…
    • 我认为限制不是硬性限制,而是在使用此类属性时要考虑的限制。请记住,被索引的是列表中每个元素的一条记录(因此,如果同一实体中有多个重复属性,它会迅速爆炸)。恕我直言,重复属性不应用于一对多,而应仅用于一对多(实际的一对多关系应标准化)或实际(有限)值,而不是关系
    【解决方案2】:

    除了被课程引用之外,会员和讲师属于俱乐部是非常有意义的。考虑用例,会员在加入俱乐部提供的课程之前需要加入俱乐部(当然这可能同时发生)。直接(在俱乐部中)引用成员/讲师,而不是每次都遍历课程,效率也显着提高。您的权衡来自额外的代码维护,但在这种情况下这似乎是合理的。

    关于活动课程过滤器,您可能希望考虑俱乐部的“活动”课程列表和“非活动”课程列表:一旦不再提供课程,将其移至“非活动”列表(并删除它来自“活动”列表)。

    【讨论】:

    • 感谢您的建议,您实际上是正确的成员列表。
    猜你喜欢
    • 1970-01-01
    • 2015-11-23
    • 2018-11-25
    • 1970-01-01
    • 2012-08-04
    • 1970-01-01
    • 1970-01-01
    • 2011-10-31
    • 2013-01-22
    相关资源
    最近更新 更多