python - 使用 SQLAlchemy 和 Flask jsonify 返回 JSON 格式的连接表

标签 python json flask sqlalchemy flask-sqlalchemy

我正在 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 新手,所以我不能 100% 确定我想要完成的任务是否已经由框架或 sqlalchemy 解决。

最佳答案

通过在 Item 中声明 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.serialized 时可能会意外地导致额外的数据库调用。

无论如何,我们确实想要列出一个类别中的所有项目,因此我们需要在另一个方向上使用外键关系。这是通过将 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 JOIN 提前获取项目,以便 c.items 不会导致额外的数据库查询。 (感谢伊尔贾)

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

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'}]}

关于python - 使用 SQLAlchemy 和 Flask jsonify 返回 JSON 格式的连接表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50011349/

相关文章:

java - jackson JSON 翻译

c# - 在 PropertyGrids 中是否有更好的 StringCollection 编辑器?

python - json 值在 if in 内按键进行打印,但不能没有

python - 在 python 中索引列表并循环这些

java - Elasticsearch Java API addMapping() 和 setSettings() 用法

python - 提供存储在 SQLAlchemy LargeBinary 列中的图像

python - 使用 SQLAlchemy 获取页面的特定数量的结果

python - 如何在CSS文件中添加图片?我正在使用 python flask 进行网站开发

python - 为什么同样的字符串在python2和python3中print的输出不同?

Python 3 tkinter x 使用箭头滚动非常慢