79

我已经有一个现有的数据库,其中有很多表和MySQL. 我打算创建一个Flask应用程序并与它一起使用 sqlalchemy。现在我在 irc 上询问并在谷歌上环顾四周并尝试了以下想法:

首先,我使用sqlacodegen从我的DB. 但后来我有点困惑,又看了看。我发现了这个

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

所以第二,我models.py根据那里的解决方案重写了我的,现在我更加困惑了。我正在寻找与现有数据库一起构建此烧瓶应用程序的最佳方法。

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

请注意,这是我使用 的第一天Flask,但​​我有使用 的经验Django,因此基本概念不是障碍。在为这个用例选择最佳方法时,我需要一些指导。详细的解释将不胜感激。详细地说,我绝对不希望有人编写所有代码并用勺子喂我,但足以让我开始,那就是将这个数据库无缝集成到flaskviasqlalchemy中。请注意,我的数据库位于MySQL.

4

10 回答 10

104

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

你挣扎的地方是 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方法)并编写一个使用该控制器的视图。

于 2013-07-15T12:11:29.693 回答
67

将 Holger 的答案与烧瓶上下文联系起来的关键是它db.Model是一个declarative_baseBase. 我花了一段时间才注意到 flask-sqlalchemy文档中的这个重要句子

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

  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
    

现在你的Buildings班级将遵循现有的模式。您可以尝试dir(Buildings)在 Python shell 中查看已列出的所有列。

于 2013-09-28T08:32:27.410 回答
22

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

我使用了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/__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/models/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/除了这个之外还有一个目录。

于 2013-07-15T14:40:26.423 回答
12

我认为将现有数据库与 sqlalchemy 一起使用的最简单方法是使用AutomapBase类。文档中的示例代码如下:

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

于 2015-05-22T19:02:29.233 回答
6

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

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

我试过了,效果很好

于 2016-11-17T07:55:26.967 回答
2

这是设置 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)
于 2015-03-21T02:32:49.993 回答
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()
于 2016-01-07T14:05:44.000 回答
1

我建议使用动态类

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()
于 2020-05-28T03:47:53.073 回答
0

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

def configure(app):
    db.init_app(app)
    db.reflect(app=app)
    app.db = db
于 2020-06-05T14:47:27.123 回答
-1

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

于 2017-01-27T21:28:07.010 回答