美文网首页
flask与SQLAlchemy

flask与SQLAlchemy

作者: 浪尖的游鱼 | 来源:发表于2019-06-27 08:39 被阅读0次

书接上文flask与DB交互,我们了解到

  • 如何建立与DB——SQLite的连接
  • 如何变更逻辑,建立表与数据的关系,让资料躺入DB
  • 如何优化项目结构
  • 如何用with优化DB的连接管理
    这一章我们将用ORM的力量优化数据库操作。

什么是ORM

ORM是用来把对象模型表示的对象映射到基于SQL的关系模型数据库结构中去。简明点说,数据库中每一行就是一个对象。

示例代码

示例代码

开始SQLAlchemy

安装

> pip3 install Flask-SQLAlchemy

更加优秀的项目工程学:

  • 创建文件夹models,归类为数据结构定义以及数据操作
  • 创建文件夹resources,归类为HTTP动作(Verbs)响应操作
  • 创建文件夹templates,归类为HTML页面

每一个页面我们都可以看成这三者的结合(当然单纯的API是不需要HTML页面的)
然后我们就该迁移之前的项目,以符合上述的规范


本章的成品规格目录

接下来就迁移项目提出一些例子:
比如我们的原文件item.py

from flask_restful import Resource, reqparse
from flask_jwt import jwt_required
import sqlite3


class Item(Resource):
    TABLE_NAME = 'items'

    parser = reqparse.RequestParser()
    parser.add_argument('price',
        type=float,
        required=True,
        help="This field cannot be left blank!"
    )

    @jwt_required()
    def get(self, name):
        item = self.find_by_name(name)
        if item:
            return item
        return {'message': 'Item not found'}, 404

    @classmethod
    def find_by_name(cls, name):
        connection = sqlite3.connect('data.db')
        cursor = connection.cursor()

        query = "SELECT * FROM {table} WHERE name=?".format(table=cls.TABLE_NAME)
        result = cursor.execute(query, (name,))
        row = result.fetchone()
        connection.close()
#不完整,只截取一部分

其实很明显的可以看出来get是我们常规定义的HTTP动作(Verbs),但是find_by_name是数据库操作。如果要把item.py拆解成model和resource两个部分,归根究底就是要把这两种函数拆分下来

#model/item.py
import sqlite3
class ItemModel(db.Model):
    @classmethod
    def find_by_name(cls, name):
        connection = sqlite3.connect('data.db')
        cursor = connection.cursor()

        query = "SELECT * FROM {table} WHERE name=?".format(table=cls.TABLE_NAME)
        result = cursor.execute(query, (name,))
        row = result.fetchone()
        connection.close()
#resource/item.py
from flask_restful import Resource, reqparse
from flask_jwt import jwt_required
from models.item import ItemModel
class Item(Resource):
    TABLE_NAME = 'items'

    parser = reqparse.RequestParser()
    parser.add_argument('price',
        type=float,
        required=True,
        help="This field cannot be left blank!"
    )

    @jwt_required()
    def get(self, name):
        item = ItemModel.find_by_name(name)
        if item:
            return item
        return {'message': 'Item not found'}, 404

以上其实是一个非常简单的操作,接下来我们需要慢慢的重构整个项目,如果前期就没有区分DB操作和HTTP动作的话,这是一个挺大的工程。当重构结束时,我们引入SQLAlchemy的概念

用SQLAlchemy重构models的内容

SQLAlchemy初体验.\db.py

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

修改app.py以导入db设定

#DB的连接词
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.db'
#默认True,SQLAlchemy会记录下对象的变动,可以理解成写log
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

#此处是注册DB。这一点蛮重要的,鱼刚摸索是也是不知道怎么把DB的定义拆解出来,原来是要在此处注册DB,这样就可以在app.py文件外处理DB相关内容。
if __name__ == '__main__':
    from db import db
    db.init_app(app)

修改item.py以导入db设定

from db import db

将表映射为对象
class ItemModel(db.Model):
    __tablename__ = 'items'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80))
    price = db.Column(db.Float(precision=2))#两位小数

这样我们就把表item映射成了class:ItemModel,接下来就是以同样的方法改造user.py

改造原DB交互方法

用SQLAlchemy寻找数据

    @classmethod
    def find_by_name(cls, name):
        return cls.query.filter_by(name=name).first()
#这句语句等效于```SELECT * FROM items WHERE name = name LIMIT 1```

用SQLAlchemy insert&update数据

    def save_to_db(self):
        db.session.add(self)
        db.session.commit()
SQLAlchemy的session.add有点类似于ORACLE的MERGE,即存在更新,不存在新增

用SQLAlchemy delete数据

    def delete_from_db(self):
        db.session.delete(self)
        db.session.commit()

现在再看下自己的models\item.py,会发现简单了非常多

接着优化models\item.py对应的resources\item.py

from models.item import ItemModel
class Item(Resource):
#此处省略reqparse的定义
    def post(self, name):
        if ItemModel.find_by_name(name):
            return {'message': "An item with name '{}' already exists.".format(name)}, 400

        data = Item.parser.parse_args()

        item = ItemModel(name, **data)

        try:
            item.save_to_db()
        except:
            return {"message": "An error occurred inserting the item."}, 500

        return item.json(), 201

class ItemList(Resource):
    def get(self):
        return {'items': list(map(lambda x: x.json(), ItemModel.query.all()))}
#这句话等效于```select * from items```

下面就看大家去优化user.py了

创建表

到这边,item.py和user.py的优化基本完成了,却缺了个任务:创建表。
app.py中

api = Api(app)

#请求钩子,又称装饰器,还有一个名字,请求的中间件。作用第一次请求之前执行
@app.before_first_request
def create_tables():
#运行models,以创建不存在的表
    db.create_all()

接下来为了体现外键的作用,教学又新增了新表stores,下面粗略的说明一下

更多有关SQLAlchemy的问题

在SQLAlchemy中的映射关系
可以详细阅读这篇文章
relationship中的 lazy屬性
如下举例,教学视屏是一个一对多的关系,store是父表,item是子表,item的store_id作为外键关联store的id
store.py

from db import db


class StoreModel(db.Model):
    __tablename__ = 'stores'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80))

    items = db.relationship('ItemModel', lazy='dynamic')

item.py

from db import db

class ItemModel(db.Model):
    __tablename__ = 'items'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80))
    price = db.Column(db.Float(precision=2))

    store_id = db.Column(db.Integer, db.ForeignKey('stores.id'))
    store = db.relationship('StoreModel')

其实store_id = db.Column(db.Integer, db.ForeignKey('stores.id'))这一句话已经定义了外键关系,至于relationship的定义其实是为了在子表?或者父表?中可以取到父表?或者子表的值。具体的理解就要看上面引用的两篇文章了。
到这里SQLAlchemy给予我们的力量已经显而易见了。希望大家在没讲到的部分自行磨练。比如继续拓展store.py

总结,我们学到了:
如何规范flask工程的项目结构
什么是ORM
如何使用SQLAlchemy
一些更多的有关SQLAlchemy的知识

下一章
flask配置生产环境

回到目录

相关文章

网友评论

      本文标题:flask与SQLAlchemy

      本文链接:https://www.haomeiwen.com/subject/phetcctx.html