web-dev-qa-db-ja.com

既存のデータベースを中心にflaskアプリケーションを構築するには?

MySQLに多くのテーブルと多くのデータを持つ既存のデータベースが既にあります。 Flaskアプリを作成し、sqlalchemyを一緒に使用する予定です。今、私はircに尋ねて、グーグルを見て回って、次のアイデアを試しました:

FirstDBからモデルを生成するために sqlacodegen を使用しました。しかし、それから私はそれについて少し混乱し、もっと見ました。そして、私は this を見つけました。

これはエレガントなソリューションのように見えました。

だからSecond、私はmodels.pyそこの解決策によると、今ではさらに混乱しています。このflaskアプリと既存のDBを構築するための最良のアプローチを探しています。

flaskのドキュメントを調べましたが、既存のデータベースを使用したプロジェクトのヘルプはまったく得られませんでした。データベースを最初から作成したり、データベースを作成したりするのに役立つものがたくさんあります。しかし、私は本当に混乱しています。

Flaskでの最初の日ですが、Djangoでの経験があるため、基本的な概念はハードルではありません。このユースケースに最適なアプローチを選択するには、いくつかのガイダンスが必要です。詳細な説明をいただければ幸いです。詳細には、誰かがすべてのコードを書いて、これをスプーンで送ってくれることは絶対に期待していませんが、始めるのに十分なのは、このデータベースをflaskを介してsqlalchemyにシームレスに統合することです。 DBがMySQLにあることに注意してください。

62

あなたの質問は、flaskとはまったく関係ありません。たとえば、テンプレート、ルート、ビュー、ログオンデコレータに問題はありません。

苦労するのはSQLAlchemyです。

だから私の提案はFlaskをしばらくの間無視し、最初にSQLAlchemyに慣れる。既存のデータベースに慣れる必要があり、SQLAlchemyからデータベースにアクセスする方法。このようなことから始めましょう(Flask ask all ... yetとは関係ないことに注意してください):

_#!/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 __== '__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 =_」行では、SQLAlchemyがそれを見つけられるように、MySQLデータベースへのパスを指定する必要があります。私の場合、既存のsqlite3データベースを使用しました。

class Users(Base)」行では、MySQLデータベースの既存のテーブルのいずれかを使用する必要があります。 sqlite3データベースに「users」という名前のテーブルがあることは知っていました。

この時点で、SQLalchemyはMySQLデータベースへの接続方法を認識し、テーブルの1つを認識します。ここで、必要な他のすべてのテーブルを追加する必要があります。最後に、SQLalchemyとの関係を指定する必要があります。ここでは、1対1、1対多、多対多、親子などを意味します。 SQLAlchemyのWebサイトには、これに関するかなり長いセクションが含まれています。

「_if __== '__main__'_」行の後に、テストコードがいくつか続きます。 pythonスクリプトをインポートせずに実行します。ここで、DBセッションを作成し、非常に単純なクエリ用であることがわかります。

私の提案は、最初にSQLAlchemyのドキュメントの重要な部分、たとえば説明的なテーブル定義、関係モデル、クエリ方法について読むことです。これを知ったら、私の例の最後の部分をコントローラーに変更し(たとえば、Pythonのyieldメソッドを使用して)、そのコントローラーを使用するビューを記述できます。

89
HolgerSchurig

Holgerの答えをflaskコンテキストに接続する鍵は、db.ModelBaseのようなdeclarative_baseオブジェクトであることです。flask-sqlalchemy's ドキュメント

アプリに使用した手順は次のとおりです。

  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クラスは既存のスキーマに従います。 Python Shellでdir(Buildings)を試して、すでにリストされているすべての列を確認できます。

53
xysun

私は最近、同じことを経験しましたが、2つのデータベース間でモデルをリンクするという追加の課題がありました。

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

    __table= '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):

    __table= '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 __== '__main__':
    app.run()

run.pyを使用してアプリをローカルで実行しますが、nginx + uWSGIを使用してdev/stage/live環境でアプリを実行します。

ただし、これに加えてviews/ディレクトリもあると思います。

19
Chris McKinnel

Sqlalchemyで既存のデータベースを使用する最も簡単な方法は、 AutomapBase classを使用することだと思います。ドキュメントのサンプルコードは次のとおりです。

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="[email protected]", user=User(name="foo")))
session.commit()

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

詳細およびより複雑な使用法については、 SqlAlchemy-Automap を参照してください

10
droidmad

自動生成を使用しようとしましたが、何も機能しないか、実行できませんでした。 sqlacodegenを使用して生成コードを検索すると、 https://github.com/ksindi/flask-sqlacodegen が見つかり、コードを生成できます。

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

試しましたが、完全に機能します

3
mcolak

このソリューションは私のために働いた

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

これは、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
sequoia

alembic(flask-sqlalchemyの背後にあるツール)は、テーブルを無視するように構成できます。構成のセットアップはそれほど難しくありません。参照: https://Gist.github.com/utek/616325

0
Adam Greenhall