zoukankan      html  css  js  c++  java
  • [ SQLAlchemy ] 自我引用型的多对多关系(Self-Referential Many-to-Many Relationship)理解

    参考:

    https://www.jianshu.com/p/2c6c76f94b88

    https://madmalls.com/blog/post/followers-and-followeds/

    实现粉丝机制

    1.用户拥有自己的粉丝列表,可以查看自己的粉丝

    2.用户拥有自己的关注列表,可以查看自己关注了谁

    站在“我”的角度,我的粉丝和我关注的人都来自于User表,我们使用自引用多对多关系(Self-Referential Many-to-Many Relationship)来描述这个模型:

     现在我们把视角切换到第三人称(或者说上帝视角),我们面前有两类人:left_users 和 right_users 。根据图片我们这样来描述:

    1.站在上帝视角,我们看到 left_users 可以关注 right_users 

    2.相对的,可以看到 right_users 粉丝是 left_users 

    这里我们用一个中间表(mid_table)来储存这种关系

    数据库模型

    mid_table = Table(
        'mid_table',Base.metadata,
        Column('left_user_id',Integer,ForeignKey('users.id'),primary_key=True),
        Column('right_user_id',Integer,ForeignKey('users.id'),primary_key=True)
    )
    
    class User(Base):
        __tablename__='users'
        id = Column(Integer,primary_key=True)
        name = Column(String)
    
        right_users = relationship(
            'User',
            secondary='mid_table',
            primaryjoin=(mid_table.c.left_user_id == id),
            secondaryjoin=(mid_table.c.right_user_id == id),
            backref = 'left_users'
        )

    1. right_users:表示 left_users 关注的人,换个说法:左边列表关注了右边列表,此处的 right_users 代表右边列表。

        说明一下,左边、右边的列表里面都是实体(如下图所示),而实际中间表里面则是user_id。(*可能理解有误)

        

    2. secondary = 'mid_table' :指定中间表(或者说关联表)

    3. primaryjoin=(mid_table.c.left_user_id == id) :  user.right_users 会使用这个条件,表示查询到user关注了谁。如何解释括号内的表达式呢?   *这里在之后的详细实现部分来说明

    4. secondaryjoin=(mid_table.c.right_user_id == id) :  user.left_users 会使用这个条件,表示查询user的粉丝是谁。

    5. backref = 'left_users' : 反向引用属性,我们用user.right_users来查询user关注了谁,用user.left_users来查询user的粉丝是谁。

    详细实现

    from sqlalchemy import create_engine,Column,Integer,String,ForeignKey,Table,Text
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy.orm import relationship,sessionmaker
    
    Base = declarative_base()
    engine = create_engine('sqlite:///many_many.db',echo=False)
    Session = sessionmaker(bind=engine)
    session = Session()
    
    mid_table = Table(
        'mid_table',Base.metadata,
        Column('left_user_id',Integer,ForeignKey('users.id'),primary_key=True),
        Column('right_user_id',Integer,ForeignKey('users.id'),primary_key=True)
    )
    def insert(name):
        user = User(name = name)
        session.add(user)
        print('inser ok ...')
    
    class User(Base):
        __tablename__='users'
        id = Column(Integer,primary_key=True)
        name = Column(String)
    
        right_users = relationship(
            'User',
            secondary='mid_table',
            primaryjoin=(mid_table.c.left_user_id == id),
            secondaryjoin=(mid_table.c.right_user_id == id),
            backref = 'left_users'
        )
        def is_followed(self,user):
            '''current_user是否关注了user这个用户,网上多是flask-sqlalchemy实现,所以我自己写了一下'''
            print(
                session.query(mid_table).filter(mid_table.c.left_user_id== self.id)
                    .filter(mid_table.c.right_user_id == user.id).count()
            )
        def following(self,user):
            '''关注user这个用户'''
            self.right_users.append(user)
    
        def __repr__(self):
            return "User:name=%s" % self.name

    [ 1.初始化数据库 ]

    if __name__=='__main__':
        Base.metadata.create_all(engine)

     [ 2.向数据库新增用户信息 ]

    if __name__=='__main__':
        insert("aaa")
        insert("bbb")
        insert("ccc")
        insert("ddd")
        session.commit()

    检查数据库已经成功添加

    [ 3.让用户1关注用户2,用户3关注用户2 ]

    if __name__=='__main__':
        user1 = session.query(User).get(1)
        user2 = session.query(User).get(2)
        #让用户3关注用户2
        # user3 = session.query(User).get(3)
        # user2 = session.query(User).get(2)
        user1.following(user2)
        session.commit()

     这时候查看中间表(mid_table)

    结合这里的中间表来解释一下 数据库模型-3 的疑问:

    要查询到user关注了谁,为什么primaryjoin括号里面的表达式要这么写(mid_table.c.left_user_id == id)?

    可以这样想:结合开头的图,中间表从左到右描述的是 left_user_id 关注了谁,那么我们只需要根据 left_user_id 筛选出 user1,不就可以知道user1关注了谁了么。也就是这样(中间表的left_user_id  == user1的id)

    [ 4.查询user1关注了谁 ]

    if __name__=='__main__':
        user1 = session.query(User).get(1)
        print(
            user1.right_users 
        )

     结合上面的中间表,得出user1关注了user2,返回的结果自然是user2

    执行 user1.right_users 这一句的时候,使用了relathionship里面定义的条件表达式 primaryjoin=(mid_table.c.left_user_id == id) ,因此能够查询出user1关注了谁。

    [ 5.查询user2的粉丝是谁 ]

    if __name__=='__main__':
        user2 = session.query(User).get(2)
        print(
            user2.left_users 
        )

    有了之前的基础,再结合中间表,要查询user2的粉丝就很简单了,只需要反查就行。

    结尾:看了下官方文档和一些博客,感觉有点云里雾里,决定自己写一篇理理头绪,想尽可能形象的来说明。限于能力有限,文章中可能会有很多错误,欢迎指出

  • 相关阅读:
    SVN 使用教程
    MVC图片上传压缩
    MVC 上传下载压缩
    C# WinForm生成二维码,一维码,条形码 操作
    C#MVC生成二维码
    ajax post方式提交到.net core api
    .net core多文件上传 日志记录
    C# .net Core 文件上传
    C#.netmvc单文件上传 ajax上传文件
    详细的sql语句
  • 原文地址:https://www.cnblogs.com/remly/p/12097033.html
Copyright © 2011-2022 走看看