实体关系模型设计与查询方法

Entity Relational Model Design and Query Method

当前设计: 用户和组:Many-to-Many Table
工具:SQLAlchemy 1.0.9,Python 2.7,金字塔

我向你提出的两个问题

  1. 我不确定我是否应该在User下有group属性user属性在Group。这是一个多对多的关系;第三个 table 与两个不同的 类 相关。

  2. SQLAlchemy 有很多很好的例子讨论 .joins,但我发现这个任务很难用这个数据库设计来根据选定的用户名进行查询。 (见下文)

我设计了一个 SQLAlchemy 数据库,并且正在尝试实施从我的 table 检索数据的最佳实践,同时消除冗余。话虽如此,我也希望这是一个有效的设计,这样当我使用 Pyramid's Authorization and Authentication System 构建 group_finder 函数时,我就没有问题了。

我正在使用 CRUD 方法。截至目前:要将 user 添加到 Group,我 update_group_add_user(...) 添加用户或 update_group_remove_users(...) 删除用户。


base.py

注释掉 groups 并将 users 添加到 class Group

association_table = Table('group_user_link', Base.metadata,
    Column('group_id', Integer, ForeignKey('groups.id')),
    Column('user_id', Integer, ForeignKey('users.id')))

class User(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)
    username = Column(String(15), nullable=False, unique=True)
    email = Column(String(300))
    password = Column(String(300), nullable=False)

    #groups = relationship('Group', secondary='group_user_link', backref='users') 

    def __init__(self, username, password, firstname, lastname, email):
        self.username = username
        self.password = hashlib.sha224(password).hexdigest()
        self.email = email
        #self._groups = groups

    def __repr__(self): # code

class Group(Base):
    __tablename__ = 'groups'

    id = Column(Integer, primary_key=True)
    group_name = Column(String(50), unique=True)

    users = relationship('User', secondary='group_user_link', backref='groups')

    def __init__(self, group_name, user=None):
        if user is None: user = []
        self.group_name = group_name
        self._user = user # to group_add_user and group_remove_user

    def __repr__(self): # code

查询方法(使用CRUD):
这将打印出列表中元组中的所有关系。我只想打印出(和 return)正在输入的用户。

def retrieve_user_bygroup(self, username):
    query= self.session.query(User, Group).join(association).\
    filter(User.id == Group.id).\
    order_by(User.id).all()
    print "retrieve user by group:",  query
    return query
  1. 我会说 group 应该包含 [比方说] usersset。反之则不然。

现在,您可能需要知道给定用户的组,如果您想将此映射缓存在内存中(而不是从数据库中获取),那么我建议不要在 groupuser class 是的。而是将该依赖项提升到第三个 class,它仅定义组和用户之间的关系。

例如

class user:
    def __init__(self, name):
        self.name = name


class group:
    def __init__(self, name):
        self.name = name


class user_group_relation:
    def get_users(self, grp_name):
        # could get it from an internal map (like return g2u_mapping[grp_name]) or 
        # run a query on some DB table..
        pass

    def get_group(self, usr_name):
        # could get it from an internal map or run a query on some DB table..
        pass

    # group to user mapping
    u2g_mapping = {user('user1'): group('group1'), user('user2'): group('group1')}
    g2u_mapping = {group('group1'): [user('user1'), user('user2')]}
  1. 如果这是例如普通用户身份验证机制的表示,然后只有用户有密码,组没有。组只是一种组织事物的方式。因此,密码字段(以及任何相关功能应保留在用户中,而不是提升为基础。

我发现了两个类似的问题,它们阐明了 backref.join.querySQLAlchemy 中的用法。这有助于阐明在稍后使用 .join.filter(显示在底部)查询时,users = relationship('User', secondary='group_user_link', backref='groups') 中的 backref 行如何通过 User 访问 groups。 =33=]

Discussing目的backref:

backref means a 'poster' attribute will be added to UserPost. Actually I'd probably call that 'user'. But the point is that 'relation()' knows how to join between those two tables because of the previously defined ForeignKey.

Discussing .join.query:

.join [works] according [to] the relations, and yields 3-tuples. The arguments to the query() call are essentially the select list in sqlalchemy.

我还发现这个 Stacks answer 对使用 .any()

很有帮助
def retrieve_group_byuser(self, username):
    # retrieves group by username
    query = self.session.query(Group).\
    filter(Group.users.any(User.username == username)).all()
    print "retrieved by username:", query
    return query

然后我从 User 中删除了 groups,现在看起来像这样:

class User(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)
    username = Column(String(15), nullable=False, unique=True)
    email = Column(String(300))
    password = Column(String(300), nullable=False)

    def __init__(self, username, password, firstname, lastname, email):
        self.username = username
        self.password = hashlib.sha224(password).hexdigest()
        self.email = email

    def __repr__(self): 
        # code