代码之家  ›  专栏  ›  技术社区  ›  meddy

使用SQLAlchemy和Flask jsonify返回JSON格式的联接表

  •  1
  • meddy  · 技术社区  · 6 年前

    我正在用python构建一个端点,该端点将返回包含每个类别中所有项目的目录。我想加入两张桌子( 目录和项目 )在我的数据库中基于外键约束并以JSON格式输出。

    目前我已尝试

    @app.route('/catalog/JSON/')
    @login_required
      def getCatalog():
      categories = session.query(Category).join(Item).all()
      return jsonify(Catalog=[r.serializable for r in categories])
    

    但是,这仅返回项目数据和有关目录的数据(例如名称)。

    我的当前型号

    class Category(Base):
    __tablename__ = 'category'
    id = Column(Integer, primary_key=True)
    name = Column(String(32), nullable=False)
    
    @property
    def serializable(self):
        return {'id': self.id, 'username': self.username}
    
    class Item(Base):
    __tablename__ = 'item'
    id = Column(Integer, primary_key=True)
    name = Column(String(32), nullable=False)
    description = Column(String(255))
    user_id = Column(Integer, ForeignKey('user.id'))
    user = relationship(User)
    category_id = Column(Integer, ForeignKey('category.id'))
    category = relationship(Category)
    
    @property
    def serializable(self):
        return {
            'id': self.id,
            'name': self.name,
            'description': self.description,
            'category_id': self.category_id,
            'user_id': self.user_id
        }
    

    我是flask的新手,所以我不能百分之百确定我要完成的是框架还是sqlalchemy已经解决了问题。

    1 回复  |  直到 6 年前
        1
  •  3
  •   Alex Hall    6 年前

    通过声明 category = relationship(Category) 在里面 Item ,实例 项目 有一个 category 属性,该属性对应于数据库中的正确行。在后台,这将在必要时从数据库中提取行。在处理项目集合时,应该小心这一点,因为这可能会导致为每个项目调用一次数据库-这称为n+1问题。

    因此,要回答“如何在可序列化项中包含self.category?”,你可以直接写:

    class Item(Base):
        ...
    
        @property
        def serializable(self):
            return {
                'id': self.id,
                'name': self.name,
                ...
                'category': self.category.serializable
            }
    

    但这可能不是一个好主意,因为在编写时可能会意外导致额外的数据库调用 item.serializable

    backref 关系的参数:

    category = relationship(Category, backref='items')
    

    现在呢 Category 实例将具有 items 属性下面是如何写作 getCatalog :

    def getCatalog():
        categories = Session().query(Category).options(joinedload(Category.items)).all()
        return dict(Catalog=[dict(c.serializable, items=[i.serializable
                                                         for i in c.items])
                             for c in categories])
    

    在这里 .options(joinedload(Category.items)) 执行SQL联接以提前获取项,以便 c.items 不会导致额外的数据库查询。(谢谢Ilja)

    以下是完整演示的完整代码:

    from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy.orm import sessionmaker, relationship, joinedload
    
    engine = create_engine('sqlite://', echo=True)
    
    Session = sessionmaker(bind=engine)
    
    Base = declarative_base()
    
    
    class Category(Base):
        __tablename__ = 'category'
        id = Column(Integer, primary_key=True)
        name = Column(String(32), nullable=False)
    
        @property
        def serializable(self):
            return {'id': self.id, 'name': self.name}
    
    
    class Item(Base):
        __tablename__ = 'item'
        id = Column(Integer, primary_key=True)
        name = Column(String(32), nullable=False)
        category_id = Column(Integer, ForeignKey('category.id'))
        category = relationship(Category, backref='items')
    
        @property
        def serializable(self):
            return {'id': self.id, 'name': self.name}
    
    
    Base.metadata.create_all(engine)
    
    category1 = Category(id=1, name='fruit')
    category2 = Category(id=2, name='clothes')
    session = Session()
    session.add_all([category1, category2,
                     Item(id=1, name='apple', category=category1),
                     Item(id=2, name='orange', category=category1),
                     Item(id=3, name='shirt', category=category2),
                     Item(id=4, name='pants', category=category2)])
    session.commit()
    
    
    def getCatalog():
        categories = Session().query(Category).options(joinedload(Category.items)).all()
        return dict(Catalog=[dict(c.serializable, items=[i.serializable
                                                         for i in c.items])
                             for c in categories])
    
    
    from pprint import pprint
    
    pprint(getCatalog())
    

    回显的SQL显示只有一个SELECT被发送到数据库。实际输出为:

    {'Catalog': [{'id': 1,
                  'items': [{'id': 1, 'name': 'apple'},
                            {'id': 2, 'name': 'orange'}],
                  'name': 'fruit'},
                 {'id': 2,
                  'items': [{'id': 3, 'name': 'shirt'}, {'id': 4, 'name': 'pants'}],
                  'name': 'clothes'}]}