使用 Flask 和 SQLAlchemy 来构建 REST API

Flask

Flask 是一个很棒的框架,使您可以使用 Python 快速构建 Web 应用程序。快速,小巧,有趣。在本教程中,我们将使用Flask框架和其他一些支持工具来构建 RESTful API。

本教程的目的是从头开始理解构建Flask服务器的概念,学习如何通过对象关系映射器与SQL数据库进行通信,以及使用面向对象的设计模式设计 RESTful API。

在本教程结束时,您将能够构建 REST API,该 API 可以使用创建,读取,更新和删除功能来管理博客文章数据。

开始

我已经在GitHub上发布了本教程的完成项目,您可以在此处进行检出或通过运行以下命令将其克隆到您的计算机中。

$ git clone https://github.com/rahmanfadhil/flask-rest-api.git

然后,创建一个新的 Python 虚拟环境,并使用 Pip 安装依赖项。

$ python -m venv env
$ source env/bin/activate
(env) $ pip install -r requirements.txt

依赖关系

根据其官方文档,Flask 是一个微型框架。这意味着,与现有的其他 Web 框架不同,Flask 并没有提供很多精美的东西。

这使得 Flask 比其他人更容易学习。但是与此同时,我们经常被迫寻找自己的解决方案来解决常见问题。例如,连接到数据库,实现身份验证系统等等。

即使是,Flask 社区提供了一些有用的开源扩展,这些扩展使我们能够仅通过安装和配置这些问题来快速解决这些问题。

以下是一些我们将用来构建 API 的第三方程序包:

  • Flask SQLAlchemy:Flask 扩展添加了对SQLAlchemy的支持,这是一种对象关系映射器,使我们更容易与 SQL 数据库进行交互。
  • Flask RESTful:另一个 Flask 扩展,用于使用面向对象的设计模式快速构建 REST API。
  • Flask Marshmallow:与 Marshmallow 集成在一起的另一个 Flask 扩展名,一个用于对象序列化的 Python 库。

Flask 项目从头开始搭建

我们可以通过使用 Python venv 模块初始化一个新的虚拟环境来开始我们的项目。

$ python3 -m venv env
$ source env/bin/activate

其次,安装下列依赖包,我们可以通过运行以下命令通过 Pip 安装依赖项。

$ pip install Flask 
    Flask-SQLAlchemy 
    Flask-RESTful 
    flask-marshmallow

最后,创建一个名为main.py的新 Python 文件(或任何您想要命名的文件)。在这里,我们设置了新的 Flask 服务器。

from flask import Flask

app = Flask(__name__)

if __name__ == '__main__':
    app.run(debug=True)

让我们对其进行测试,我们可以使用以下命令执行 Python 脚本。

(env) $ python main.py

打开  http://localhost:5000,您会看到一个 404 页面。

配置数据库

在这里,我们将使用 SQL 数据库存储博客文章数据。

出于学习目的,我将使用 SQLite,这是一个小型 SQL 数据库实现,非常容易启动和运行。请记住,您可能想在生产环境中考虑更可靠的数据库,例如 PostgreSQLMySQL

要在 Flask 项目中设置 SQLAlchemy,我们可以导入flask_sqlalchemy软件包(我们之前已安装),然后将 Flask app变量包装在新的SQLAlchemy 对象。我们还希望在 Flask 应用程序配置中设置 SQLALCHEMY_DATABASE_URI 以指定我们要使用的数据库以及如何访问它。

from flask import Flask
from flask_sqlalchemy import SQLAlchemy # new

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db' # new
db = SQLAlchemy(app) # new

if __name__ == '__main__':
    app.run(debug=True)

数据库模型

模型(model)不过是数据库的表示形式,您可以在其中存储,获取和操作数据库中的数据。将其视为应用程序和数据库的中间人。它通常代表一个表或者是集合。

它使您可以直接从 Python 代码对特定表执行所有操作。这样您就无需再搞乱 SQL 查询了🙃。

让我们创建一个表示我们博客文章数据的模型。

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(50))
    content = db.Column(db.String(255))

    def __repr__(self):
        return '<Post %s>' % self.title

在这里,我们有一个id属性,它是一个自动生成的主键字段。然后,我们还有一个titlecontent字段,只是一个定义了最大长度的普通字符串字段。

这部分是可选的,但是为了确保所有内容都是显而易见的,我们可以设置一个__ repr __方法,以使每个单独的 post 对象都可打印到控制台。

然后,我们需要为我们的模型设置模式。这是必需的,因为我们想将我们的 post 对象解析为 JSON 响应。在这种情况下,我们可以使用flask_marshmallow软件包。

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow # new

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = SQLAlchemy(app)
ma = Marshmallow(app) # new

# ...

if __name__ == '__main__':
    app.run(debug=True)

根据我们的Post模型创建一个新的 marshmallow schema 。

class PostSchema(ma.Schema):
    class Meta:
        fields = ("id", "title", "content")

post_schema = PostSchema()
posts_schema = PostSchema(many=True)

在这种模式(marshmallow schema)下,我们可以选择向用户公开哪些字段。如果您的模型包含一些敏感数据,则可能需要在此处将其排除。然后,我们也在posts_schemapost_schema中实例化它。使用posts_schema序列化一系列帖子。否则,请使用post_schema

大多数人(包括我自己)通常都会忘记的下一个重要部分是通过从 Python 交互式外壳程序 (REPL) 内的 SQLAchemy 实例调用db.create_all函数来初始化数据库模式。之所以在 REPL 中执行此操作,是因为我们只需要初始化一次架构。

另外,请确保您处于正确的 Python 环境中。

(env) $ python
>>> from main import db
>>> db.create_all()
>>> exit()

RESTful 路由

最后,我们可以开始定义 RESTful 处理程序。我们将使用 Flask-RESTful 软件包,这是一组工具,可帮助我们使用面向对象的设计来构建 RESTful 路由。

我们需要设置 Flask-RESTful 扩展名才能在 Flask 服务器中启动并运行。

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_restful import Api, Resource # new

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = SQLAlchemy(app)
api = Api(app) # new

# ...

if __name__ == '__main__':
    app.run(debug=True)

创建新的 RESTful 资源,以这种方式,我们定义业务逻辑,例如如何从数据库中获取数据,如何进行身份验证等等。

class PostListResource(Resource):
    def get(self):
        posts = Post.query.all()
        return posts_schema.dump(posts)

api.add_resource(PostListResource, '/posts')

在这里,我们接受GET请求并进行查询以获取具有Post模型的所有帖子。然后,我们利用posts_schema序列化数据库中的数据并将其作为对客户端的响应返回。

最后,我们使用api.add_resource方法注册资源并定义路由端点。

启动服务器,将请求发送到posts端点,您将获得一个空数组。

$ curl http://localhost:5000/posts
[]

太好了👌,现在该进行创建操作了。

# ...
from flask import Flask, request # change

# ...

class PostListResource(Resource):
    def get(self):
        posts = Post.query.all()
        return posts_schema.dump(posts)

    # new
    def post(self):
        new_post = Post(
            title=request.json['title'],
            content=request.json['content']
        )
        db.session.add(new_post)
        db.session.commit()
        return post_schema.dump(new_post)

# ...

我们在资源中创建一个新的post方法,使用请求数据实例化一个新的 post 对象,并将记录保存到数据库中。最后,我们将发布数据作为响应返回给客户端。

尝试使用发布数据将 POST 请求发送到/ todos端点。

$ curl http://localhost:5000/posts \
    -X POST \
    -H "Content-Type: application/json" \
    -d '{"title":"Post 1", "content":"Lorem ipsum"}'
{
    "content": "Lorem ipsum",
    "id": 1,
    "title": "Post 1"
}

让我们创建一个新资源来获取单个 Post:

class PostResource(Resource):
    def get(self, post_id):
        post = Post.query.get_or_404(post_id)
        return post_schema.dump(post)

api.add_resource(PostResource, '/posts/<int:post_id>')

在这里,我们也接受了GET请求,但是我们不查询所有帖子,而是获取具有给定 Id 的单个帖子。如果不存在,则会引发 404 错误。

尝试获取/ todos / <int:id>,您将获得我们刚刚创建的 Post。

$ curl http://localhost:5000/posts/1
{
    "title": "Post 1",
    "content": "Lorem ipsum",
    "id": 1
}

而且,如果您请求发布的 ID 不存在,可能还是会收到 404 错误🙃。

$ curl http://localhost:5000/posts/12 -I
HTTP/1.0 404 NOT FOUND
...

现在,让我们添加其余的 CRUD 操作,进行更新和删除。

class PostResource(Resource):
    # ...

    def patch(self, post_id):
        post = Post.query.get_or_404(post_id)

        if 'title' in request.json:
            post.title = request.json['title']
        if 'content' in request.json:
            post.content = request.json['content']

        db.session.commit()
        return post_schema.dump(post)

    def delete(self, post_id):
        post = Post.query.get_or_404(post_id)
        post.delete()
        db.session.commit()
        return '', 204

patch方法中,我们首先获取 Post 对象(如果存在),然后更新请求正文中定义的属性(request.json)。这就是为什么我们需要在表达式中使用检查这两个属性。使用db.session.commit()将更改保存到数据库中,并将更新数据发送到客户端。

delete方法中,我们还获得了post对象。但是这次,我们只是从发布对象中使用delete方法删除对象。保存更改,不向客户端返回任何内容(因为没有要显示的内容)。

现在,我们已经启动并运行了所有功能,让我们对其进行测试!

更新帖子:

$ curl http://localhost:5000/posts/1 \
    -X PATCH \
    -H "Content-Type: application/json" \
    -d '{"title":"Updated Post", "content":"Updated post content"}'
{
    "content": "Updated post content",
    "id": 1,
    "title": "Updated Post"
}

删除帖子:

$ curl http://localhost:5000/posts/1 -X DELETE -I
HTTP/1.0 204 NO CONTENT
...
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://dev.to/rahmanfadhil/build-rest-a...

译文地址:https://learnku.com/python/t/38900

本文为协同翻译文章,如您发现瑕疵请点击「改进」按钮提交优化建议
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!