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

时间:2013-07-15 11:14:10

标签: python mysql sqlalchemy flask flask-sqlalchemy

我已经有一个现有的数据库,在MySQL中有很多表和大量数据。我打算创建一个Flask应用程序并使用sqlalchemy。现在我问了irc并在google上四处寻找并尝试了以下想法:

首先我使用sqlacodegen从我的DB生成模型。但后来我对此感到困惑,看了一下。我找到了this

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

所以第二次,我根据那里的解决方案改写了我的models.py,现在我更加困惑了。我正在寻找最好的方法来构建这个烧瓶应用程序以及现有的数据库。

我查看了烧瓶文档,但对于已有db的项目没有真正得到任何帮助。从头开始创建内容有很多好东西,创建数据库和所有内容。但我真的很困惑。

请注意,这是Flask的第一天,但​​我有Django的经验,所以基本概念不是障碍。我需要一些指导来为这个用例选择最佳方法。非常感谢详细解释。通过详细说明,我绝对不希望有人写下所有的代码和勺子喂我这个,但足以让我开始,这是通过flask无缝地将这个数据库集成到sqlalchemy。请注意我的数据库位于MySQL

10 个答案:

答案 0 :(得分:83)

我说你的问题与烧瓶完全无关。例如,模板,路径,视图或登录装饰器没有问题。

你在哪里挣扎在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”的表。

在此之后,SQLalchemy知道如何连接到MySQL数据库,并且知道其中一个表。您现在需要添加您关心的所有其他表。最后,您需要指定与SQLalchemy的关系。在这里,我指的是像一对一,一对多,多对多,亲子等等。 SQLAlchemy网站包含一个相当冗长的部分。

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

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

答案 1 :(得分:46)

将Holger的答案与瓶子上下文联系起来的关键是db.Model是一个declarative_base对象,如Base。我花了一段时间才注意到flask-sqlalchemy的documentation

中的这个重要句子

以下是我用于我的应用的步骤:

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

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

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

    class Buildings(db.Model):
        __table__ = db.Model.metadata.tables['BUILDING']
    
        def __repr__(self):
            return self.DISTRICT
    
  4. 现在,您的Buildings课程将遵循现有架构。您可以在Python shell中尝试dir(Buildings)并查看已列出的所有列。

答案 2 :(得分:18)

我最近经历了同样的事情,还有将模型链接到两个数据库的额外挑战。

我使用了Flask-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 / ___初始化___。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_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)
    ...

<强> run.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/目录。

答案 3 :(得分:9)

我认为使用sqlalchemy的现有数据库的最简单方法是使用AutomapBase类。 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-Automap

答案 4 :(得分:3)

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

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

我试过,它完美无缺

答案 5 :(得分:1)

这是设置Holger答案中描述的引擎路径的另一种方法。如果您的用户名或密码中有特殊字符,则非常方便。

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)

答案 6 :(得分:1)

此解决方案适合我

"""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()

答案 7 :(得分:0)

可以将 alembic(flask-sqlalchemy背后的工具)配置为忽略表。配置并不难设置。见:https://gist.github.com/utek/6163250

答案 8 :(得分:0)

我建议使用动态类

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()

答案 9 :(得分:0)

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

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