Comment construire une application flask autour d'une base de données déjà existante?

J'ai déjà une base de données existante qui contient beaucoup de tables et beaucoup de données dans MySQL. J'ai l'intention de créer une application Flask et d'utiliser sqlalchemy avec elle. Maintenant, j'ai demandé sur irc et regardé autour de google et essayé les idées suivantes:

D'Abord j'ai utilisé sqlacodegen pour générer les modèles à partir de mon DB. Mais alors j'étais confus à ce sujet un peu et regardé un peu plus. Et j'ai trouvé ce.

Cela ressemblait à un élégant solution.

Donc deuxième , j'ai réécrit mon models.py selon la solution là-bas et maintenant je suis encore plus confus. Je suis à la recherche de la meilleure approche pour construire cette application flask avec la base de données déjà existante.

J'ai regardé dans la documentation de flask mais je n'ai pas vraiment eu d'aide pour un projet avec une base de données déjà existante. Il y a beaucoup de bonnes choses pour créer quelque chose à partir de zéro, créer la base de données et tout. Mais je suis vraiment confus.

Veuillez noter que son mon premier jour avec Flask, mais j'ai de l'expérience avec Django, donc les concepts de base ne sont pas un obstacle. J'ai besoin de conseils pour choisir la meilleure approche pour ce cas d'utilisation. Une explication détaillée serait grandement apprécié. En détail, Je ne m'attends certainement pas à ce que quelqu'un écrive tout le code et me nourrisse à ce sujet, mais juste assez pour me lancer, c'est-à-dire intégrer cette base de données de manière transparente dans flask via sqlalchemy. Notez que ma base de données est dans MySQL.

52
demandé sur AndreL 2013-07-15 15:14:09

8 réponses

Je dirais que votre question n'a rien à voir avec flask. Par exemple, vous n'avez pas de problème avec les modèles, les routes, les vues ou les décorateurs d'ouverture de session.

Où vous luttez est à SQLAlchemy.

Donc, ma suggestion est d'ignorer Flask pendant un moment et de s'habituer à SQLAlchemy en premier. Vous devez vous habituer à votre base de données existante et comment y accéder à partir de SQLAlchemy. Utilisez un outil de documentation MySQL pour trouver votre chemin autour de cela. Le début avec quelque chose comme ça (notez que cela n'a rien à voir avec Flask ask all ... encore):

#!/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

Dans la ligne "engine = " vous devez fournir votre chemin vers votre base de données MySQL, de sorte que SQLAlchemy le trouve. Dans mon cas, j'ai utilisé une base de données sqlite3 préexistante.

Dans la ligne "class Users(Base)", vous devez utiliser l'une des tables existantes dans votre base de données MySQL. Je savais que ma base de données sqlite3 avait une table nommée "utilisateurs".

Après ce point, SQLalchemy sait comment se connecter à votre base de données MySQL et il connaît l'une des tables. Vous devez maintenant ajouter toutes les autres tables que vous aimez. Enfin, vous devez spécifier des relations avec SQLalchemy. Ici, je veux dire des choses comme un à un, un à plusieurs, plusieurs à plusieurs, parent-enfant et ainsi de suite. Le site Web SQLAlchemy contient une section assez longue à ce sujet.

Après la ligne "if __name__ == '__main__' " vient juste un code de test. Il sera exécuté si je n'importe pas mon script python, mais exécutez. Ici vous voyez que je crée une session DB et est-ce pour une requête très simple.

Mon la suggestion est que vous lisiez d'abord les parties importantes de la documentation de SQLAlchemy, par exemple la définition de la table descriptive, le modèle de relation et la façon d'interroger. Une fois que vous le savez, vous pouvez changer la dernière partie de mon exemple en un contrôleur (par exemple en utilisant la méthode yield de Python) et écrire une vue qui utilise ce contrôleur.

74
répondu HolgerSchurig 2018-04-19 07:12:35

La clé pour connecter la réponse de Holger à un contexte flask est que db.Model est un objet declarative_base comme Base. Il m'a fallu du temps pour remarquer cette phrase importante dans la documentation de flask-sqlalchemy

Voici les étapes que j'ai utilisées pour mon application:

  1. Initiez un objet db de la manière habituelle flask-alchemy: db = SQLAlchemy(app). Notez que vous devrez définir app.config['SQLALCHEMY_DATABASE_URI'] = 'connection_string' avant cela.

  2. Lier la base déclarative à un moteur: db.Model.metadata.reflect(db.engine)

  3. Alors vous peut utiliser les tables existantes facilement (par exemple. J'ai une table appelée bâtiments):

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

Maintenant, votre classe Buildings suivra le schéma existant. Vous pouvez essayer dir(Buildings) dans un shell Python et voir toutes les colonnes déjà listées.

37
répondu xysun 2018-04-19 07:17:59

J'ai récemment vécu la même chose, avec le défi supplémentaire de relier les modèles à travers deux bases de données.

J'ai utilisé Flask-SQLAlchemy et tout ce que j'avais à faire était de définir mes modèles de la même manière que mes tables de base de données, et j'étais parti rire. Ce que j'ai trouvé difficile, c'était de déterminer exactement à quoi devrait ressembler la structure de mon projet.

Mon projet était une API Restful, et c'est ce que j'ai fini avec:

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

Fichiers de remarque:

Conf/xxx.py

C'est ainsi que nous disons à Flask-SQLAlchemy à quoi se connecter, plus vous pouvez mettre n'importe quel autre élément de configuration ici (comme l'emplacement du journal, la configuration du débogage, etc.).

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

App_name/___init___.py

C'est là que je crée mon application et initialise la base de données. Cet objet db sera importé et utilisé dans toute l'application (c'est-à-dire dans les modèles, les tests, etc.). J'ai également défini mon enregistreur, initialise mes API et blueprints et attache mon middleware ici (non illustré).

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

Run.py

#! /usr/bin/env python

from app_name import create_app

app = create_app(env='local')

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

J'utilise run.py pour exécuter l'application en local, mais j'utilise nginx + uWSGI pour exécuter l'application dans la dev/stage/milieux de vie.

Je suppose que vous aurez un répertoire views/ en plus de cela.

17
répondu Chris McKinnel 2014-06-18 20:54:30

Je pense que le moyen le plus simple d'utiliser une base de données existante avec sqlalchemy est d'utiliser la classe AutomapBase. Un exemple de code de docs est le suivant:

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)

Reportez-vous à SqlAlchemy-Automap pour plus de détails et des utilisations plus compliquées

9
répondu droidmad 2015-05-22 19:02:29

J'essaie d'utiliser générée automatiquement, mais rien ne fonctionne ou je ne pouvais pas l'exécuter. Lorsque je cherche à générer du code en utilisant sqlacodegen, je trouve https://github.com/ksindi/flask-sqlacodegen , vous pouvez générer le code juste

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

J'ai essayé et ça marche parfaitement

3
répondu mcolak 2016-11-17 07:55:26

C'est une autre façon de configurer le chemin du moteur décrit dans la réponse de Holger. Pratique s'il y a des caractères spéciaux dans votre nom d'utilisateur ou mot de passe.

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
répondu sequoia 2015-03-21 02:32:49

Cette solution a fonctionné pour moi

"""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
répondu 2016-01-07 14:05:44

Alambic (l'outil derrière flacon-sqlalchemy) peut être configuré pour ignorer les tables. La configuration n'est pas trop difficile à configurer. voir: https://gist.github.com/utek/6163250

0
répondu Adam Greenhall 2017-01-27 21:28:07