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


69

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

首先,我使用sqlacodegen从我的生成模型DB。但是后来我对此有些困惑,看上去有些困惑。我发现了这个

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

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

我查看了flask文档,但对于具有现有数据库的项目并没有真正获得任何帮助。从头开始创建东西,创建数据库以及所有东西都有很多好东西。但是我真的很困惑。

请注意,这是我的第一天工作Flask经验,但我有的经验Django,因此基本概念并非障碍。在为该用例选择最佳方法时,我需要一些指导。详细的解释将不胜感激。详细地说,我绝对不希望有人编写所有代码并为此而spoon之以鼻,但这足以使我起步,即将该数据库无缝集成到flaskvia中sqlalchemy。注意我的数据库在MySQL

Answers:


95

我想说你的问题与烧瓶完全无关。例如,您对模板,路线,视图或登录修饰符没有任何问题。

您在其中挣扎的地方是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数据库有一个名为“用户”的表。

此后,SQLalchemy知道如何连接到MySQL数据库,并且知道其中一个表。现在,您需要添加所需的所有其他表。最后,您需要指定与SQLalchemy的关系。在这里,我的意思是像一对一,一对多,多对多,亲子等等。SQLAlchemy网站包含有关此内容的简短内容。

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

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


非常感谢您的描述性回答。我一直在阅读sqlalchemy文档,特别是关系部分。到目前为止,我已经实施了您的解决方案,您的回答似乎使我从被困的那一刻起得到了推动,但是我想知道,如何将其集成到烧瓶中?我的意思是在flask文档中,有很多关于init_db()专门创建数据库的内容。那这种情况呢?谢谢阿吉安。:)
Indradhanush Gupta

然后,当您想在Flask中进行操作时:flask.pocoo.org/docs/patterns/sqlalchemy/#declarative
Rohmer

1
sqlalchemy文档中有一节介绍了如何为各种不同的数据库配置引擎路径:docs.sqlalchemy.org/en/rel_0_9/core/engines.html,也可以使用关键字而不是将关键字设置为引擎路径使用URL选项的单个字符串:docs.sqlalchemy.org/en/rel_0_9/core/…,我的密码具有特殊字符,这很有用
sequoia

StackOverflow上存在的最有用的注释之一
ccdrm

61

将Holger的答案与Flask上下文联系起来的关键db.Model是一个declarative_base类似的对象Base。花了我一段时间来注意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中尝试查看已列出的所有列。


4
关于“ db.Model是像Base这样的declarative_base对象”的见解使我大吃一惊。在我读到这篇文章之前,我还不知道要在烧瓶炼金术中做到这一点。谢谢。
ISONecroMAn

4
当谈到Flask-sqlalchemy db.Model时,类似于Base。db.session与会话类似。
ISONecroMAn

请在上面写博客,网络上可能会有误导性的答案,其他地方都
没有找到

20

最近,我经历了同样的事情,另外一个挑战是在两个数据库之间链接模型。

我使用了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

这是我创建我的应用程序并初始化数据库的地方。该数据库对象将被导入并在整个应用程序中使用(即在模型,测试等中)。我还设置了记录器,初始化了我的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)
    ...

运行

#! /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/除了那里之外还会有一个目录。


为什么要models/分开?心不是models.py里面app/是否足够?
Indradhanush Gupta

1
不,我有很多模型,所以我将它们分成了单独的文件。
克里斯·麦金奈尔

您提到您的模型涵盖了多个数据库,但是对我来说,如何处理它并不是立即显而易见的,因为上述内容似乎仅反映了一个数据库对象?我误会了吗?
W.Prins

11

我认为将现有数据库与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


5

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

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

我尝试了,效果很好


我正在尝试从终端连接字符串下面连接到我的本地sql服务器实例flask-sqlacodegen --outfile models.py mssql:// user:password @ localhost / db_name我收到错误“找不到数据源”并且未指定默认驱动程序。我是unixodbc,已安装FREETDS,并能够以编程方式并通过isql,tsql命令进行连接。
Wonderful先生

我要衷心感谢您向我介绍了这个精彩的模块。处理数据库中的复杂关系时的救生员。
9

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)

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

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

0

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

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

-1

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


1
我认为您会发现Alembic的作用与问题相反。问题是要使模型反映数据库,而Alembic则要使数据库反映模型。另外,Alembic强调了Flask-Migrate库,而不是Flask-SQLAlchemy。
SuperShoot
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.