如何围绕现有数据库构建烧瓶应用程序?

我已经有一个现有的数据库,有很多表和大量的数据在 MySQL。我打算创建一个 Flask应用程序,并与它一起使用 sql 炼金术。现在,我在 IRC 上发问,在谷歌上查找,并尝试了以下想法:

首先 我使用 (咒语)从我的 DB生成模型。但后来我对此有点困惑,于是又看了一会儿。我找到了 这个

这看起来是一个优雅的解决方案。

所以 第二,我根据那里的解重写了我的 models.py,现在我更加困惑了。我正在寻找最好的方法来建立这个烧瓶应用与已经存在的数据库。

我查看了烧瓶文档,但是对于一个已经存在数据库的项目,我没有得到任何帮助。有很多好东西可以从头开始创建,创建数据库等等。但我真的很困惑。

请注意,这是我第一天与 Flask,但我有经验与 Django,所以基本概念不是一个障碍。在为这个用例选择最佳方法方面,我需要一些指导。如能详细解释,将不胜感激。通过详细说明,我当然不指望有人写所有的代码和勺子喂我在这一点上,但只是足以让我开始,这是集成这个数据库到 flask通过 sqlalchemy无缝。注意我的尸体在 MySQL

62620 次浏览

我觉得你的问题和酒瓶没有任何关系。例如,模板、路由、视图或登录修饰符没有问题。

你挣扎的地方在 SQLAlchemy。

因此,我的建议是暂时忽略 Flask,先适应 SQLAlchemy。您需要习惯现有的数据库以及如何从 SQLAlchemy 访问它。使用一些 MySQL 文档工具来找到解决这个问题的方法。以这样的内容开始(注意,它与 Flask 没有任何关系) :

#!/usr/bin/python
# -*- mode: python -*-


from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base


engine = create_engine('sqlite:///webmgmt.db', convert_unicode=True, echo=False)
Base = declarative_base()
Base.metadata.reflect(engine)




from sqlalchemy.orm import relationship, backref


class Users(Base):
__table__ = Base.metadata.tables['users']




if __name__ == '__main__':
from sqlalchemy.orm import scoped_session, sessionmaker, Query
db_session = scoped_session(sessionmaker(bind=engine))
for item in db_session.query(Users.id, Users.name):
print item

在“ engine =”行中,您需要提供到 MySQL 数据库的路径,以便 SQLAlchemy 找到它。在我的例子中,我使用了一个预先存在的 sqlite3数据库。

在“ class Users(Base)”行中,您需要使用 MySQL 数据库中的一个现有表。我知道我的 sqlite3数据库有一个名为“ users”的表。

在此之后,SQLtancy 知道如何连接到 MySQL 数据库,并且知道其中一个表。现在,您需要添加所关心的所有其他表。最后,您需要指定与 SQL 炼金术的关系。这里我指的是一对一,一对多,多对多,亲子关系等等。SQLAlchemy 网站上有一个相当长的章节。

在“ if __name__ == '__main__'”行之后是一些测试代码。如果我不导入 python 脚本,而是运行,那么它将被执行。在这里您可以看到我创建了一个 DB 会话,这是一个非常简单的查询。

我的建议是您首先阅读 SQLAlchemy 文档的重要部分,例如描述性表定义、关系模型以及如何查询。一旦您了解了这一点,您就可以将我的示例的最后一部分更改为控制器(例如,使用 Python 的 yield方法) ,并编写使用该控制器的视图。

我最近也经历了同样的事情,但是在跨两个数据库链接模型时遇到了额外的挑战。

我使用的是 烧瓶-SQLAlchemy,所有我必须做的就是定义我的模型,就像我的数据库表看起来那样。我发现很难搞清楚我的项目结构应该是什么样子。

我的项目是 Restful API,这是我最终得到的结果:

conf/
__init__.py
local.py
dev.py
stage.py
live.py
deploy/
#nginx, uwsgi config, etc
middleware/
authentication.py
app_name/
blueprints/
__init__.py
model_name.py #routes for model_name
...
models/
__init.py
model_name.py
__init__.py
database.py
tests/
unit/
test_etc.py
...
run.py

注意事项:

Conf/xxx.py

这是我们告诉 Flask-SQLAlchemy 要连接到什么的方式,另外您可以在这里放置任何其他配置项(比如日志位置、调试配置等)。

SQLALCHEMY_DATABASE_URI = 'mysql://username:password@host:port/db_name'

App _ name/_ _ init _ _. py

这是我创建我的应用程序和初始化数据库的地方。这个 db 对象将被导入并在整个应用程序中使用(例如,在模型、测试等中)。我还设置我的日志,初始化我的 API 和蓝图,并附加我的中间件在这里(未显示)。

from app_name.database import db
from flask import Flask


def create_app(*args, **kwargs):
env = kwargs['env']
app = Flask(__name__)
app.config.from_object('conf.%s' % env)
db.init_app(app)
return app

App _ name/database. py

from flask.ext.sqlalchemy import SQLAlchemy
db = SQLAlchemy()

App _ name/model/model _ name. py

from services.database import db




class Bar(db.Model):


__tablename__ = 'your_MySQL_table_name'


id = db.Column('YourMySQLColumnName', db.Integer, primary_key=True)
name = db.Column('WhateverName', db.String(100))
foo = db.Column(db.ForeignKey('another_MySQLTableName.id'))


class Foo(db.Model):


__tablename__ = 'another_MySQLTableName'


id = db.Column('FooId', db.Integer, primary_key=True)
...

跑吧 Py

#! /usr/bin/env python


from app_name import create_app


app = create_app(env='local')


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

我使用 run.py在本地运行应用程序,但是我使用 nginx + uWSGI 在 dev/stage/live 环境中运行应用程序。

我猜除了这个,你还有一个 views/目录。

将 Holger 的答案与烧瓶上下文联系起来的关键是,db.Model是一个类似于 Basedeclarative_base对象。我花了一段时间才注意到烧瓶里的这个重要句子-- 烧瓶炼金术的 文件

下面是我的应用程序使用的步骤:

  1. 以通常的烧瓶炼金术方式启动一个 db对象: db = SQLAlchemy(app)。注意你需要在此之前设置 app.config['SQLALCHEMY_DATABASE_URI'] = 'connection_string'

  2. 将声明基绑定到引擎: db.Model.metadata.reflect(db.engine)

  3. 然后,您可以轻松地使用现有的表(例如,我有一个名为 BUILDING 的表) :

    class Buildings(db.Model):
    __table__ = db.Model.metadata.tables['BUILDING']
    
    
    def __repr__(self):
    return self.DISTRICT
    

Now your Buildings class will follow the existing schema. You can try dir(Buildings) in a Python shell and see all the columns already listed.

这是另一种方法来设置发动机路径描述霍尔格的答案。如果您的用户名或密码中有特殊字符,则很方便。

from sqlalchemy.engine.url import URL
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base


engine_URL = URL('mssql+pymssql',
username='DOMAIN\\USERNAME',
password="""p@ssword'!""",
host='host.com',
database='database_name')


engine = create_engine(engine_URL)
Base = declarative_base()
Base.metadata.reflect(engine)

我认为使用现有数据库的最简单的方法是使用 自动化数据库类。来自 docs 的示例代码如下:

from sqlalchemy.ext.automap import automap_base
from sqlalchemy.orm import Session
from sqlalchemy import create_engine


Base = automap_base()


# engine, suppose it has two tables 'user' and 'address' set up
engine = create_engine("sqlite:///mydatabase.db")


# reflect the tables
Base.prepare(engine, reflect=True)


# mapped classes are now created with names by default
# matching that of the table name.
User = Base.classes.user
Address = Base.classes.address


session = Session(engine)


# rudimentary relationships are produced
session.add(Address(email_address="foo@bar.com", user=User(name="foo")))
session.commit()


# collection-based relationships are by default named
# "<classname>_collection"
print (u1.address_collection)

有关详细信息和更复杂的用法,请参阅 SqlAlchemy-自动地图

这个办法对我很有效

"""Example for reflecting database tables to ORM objects


This script creates classes for each table reflected
from the database.


Note: The class names are imported to the global namespace using
the same name as the tables. This is useful for quick utility scripts.
A better solution for production code would be to return a dict
of reflected ORM objects.
"""


from sqlalchemy import create_engine, MetaData
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base




def reflect_all_tables_to_declarative(uri):
"""Reflects all tables to declaratives


Given a valid engine URI and declarative_base base class
reflects all tables and imports them to the global namespace.


Returns a session object bound to the engine created.
"""


# create an unbound base our objects will inherit from
Base = declarative_base()


engine = create_engine(uri)
metadata = MetaData(bind=engine)
Base.metadata = metadata


g = globals()


metadata.reflect()


for tablename, tableobj in metadata.tables.items():
g[tablename] = type(str(tablename), (Base,), {'__table__' : tableobj })
print("Reflecting {0}".format(tablename))


Session = sessionmaker(bind=engine)
return Session()




# set to database credentials/host
CONNECTION_URI = "postgres://..."


session = reflect_all_tables_to_declarative(CONNECTION_URI)


# do something with the session and the orm objects
results = session.query(some_table_name).all()

我尝试使用自动生成,但没有工作或我不能运行它。当我使用 sqlacodegen 搜索生成代码时,我找到了 https://github.com/ksindi/flask-sqlacodegen,您可以只生成

flask-sqlacodegen  mysql://username:password@host:port/db_name --schema yourschema --tables table1,table2 --flask

我试过了,效果很好

Alembic (flask-sqltancy 背后的工具)可以配置为忽略表

我建议使用动态类

from flask import Flask
from sqlalchemy import Table, MetaData
from flask_sqlalchemy import SQLAlchemy
import os


app = Flask(__name__)


class Config(Object):
basedir = os.path.abspath(os.path.dirname(__file__))
SQLALCHEMY_DATABASE_URI = 'sqlite:///' +  + os.path.join(basedir, 'db.sqlite')
SQLALCHEMY_TRACK_MODIFICATIONS = False


app.config.from_object(config)
db = SQLAlchemy(app)
metadata = MetaData()


table_reflection = Table("table_name", metadata, autoload=True, autoload_with=db.engine)
attrs = {"__table__": table_reflection}
TableModel = type("table_name", (db.Model,), attrs)

现在,TableModel 可以用作模型类

# Querying
TableModel.query.all()
TableModel.query.get(1)
TableModel.query.filter_by(id=2).first()


# insertion
new_row = TableModel()
db.session.add(new_row)
db.session.commit()

如果您的应用程序是简单的,您可以在您的模型文件中使用 db.reflect(app):

def configure(app):
db.init_app(app)
db.reflect(app=app)
app.db = db