web-dev-qa-db-fra.com

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 a 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 j'ai regardé autour de Google et j'ai essayé les idées suivantes:

D'abord J'ai utilisé sqlacodegen pour générer les modèles à partir de mon DB. Mais ensuite, je m'en suis un peu perdu et j'ai regardé un peu plus. Et j'ai trouvé ceci .

Cela ressemblait à une solution élégante.

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

J'ai regardé la documentation flask mais je n'ai pas vraiment obtenu 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 c'est 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ée. En détail, je ne m'attends certainement pas à ce que quelqu'un écrive tout le code et la cuillère me nourrit à 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.

62
Indradhanush Gupta

Je dirais que votre question n'a rien à voir avec flask du tout. Par exemple, vous n'avez pas de problème avec les modèles, les itinéraires, les vues ou les décorateurs de connexion.

Où vous vous débattez est à SQLAlchemy.

Donc, ma suggestion est d'ignorer Flask pendant un certain temps et de vous habituer à SQLAlchemy d'abord. 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. Commencez par quelque chose comme ça (notez que cela n'a rien à voir avec Flask demandez à tous ... pour le moment)):

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

Dans la ligne "engine =", Vous devez fournir votre chemin d'accès à votre base de données MySQL, afin 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 connaît l'une des tables. Vous devez maintenant ajouter toutes les autres tables dont vous vous souciez. Enfin, vous devez spécifier les 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 __== '__main__'" Vient juste un code de test. Il sera exécuté si je n'importe pas mon script python, mais que je l'exécute. Ici, vous voyez que je crée une session DB et que c'est pour une requête très simple.

Ma suggestion est que vous lisez 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 comment interroger. Une fois que vous savez cela, vous pouvez changer la dernière partie de mon exemple en contrôleur (par exemple en utilisant la méthode yield de Python) et écrire une vue qui utilise ce contrôleur.

89
HolgerSchurig

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 un certain temps pour remarquer cette phrase importante dans le flask-sqlalchemy's documentation

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

  1. lancer un objet db de la manière habituelle d'alchimie flacon: 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. Ensuite, vous pouvez facilement utiliser les tables existantes (par exemple, j'ai une table appelée BUILDINGS):

    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 Python Shell et voir toutes les colonnes déjà répertoriées.

53
xysun

J'ai récemment vécu la même chose, avec le défi supplémentaire de lier 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. Ce que j'ai trouvé difficile était de savoir 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 à noter:

conf/xxx.py

C'est ainsi que nous indiquons à Flask-SQLAlchemy à quoi se connecter, et vous pouvez y placer tous les autres éléments de configuration (comme l'emplacement du journal, la configuration de débogage, etc.).

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

nom_app/__ 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 l'ensemble de l'application (c'est-à-dire dans les modèles, les tests, etc.). J'ai également configuré mon enregistreur, initialisé mes API et plans et attaché 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

nom_app/database.py

from flask.ext.sqlalchemy import SQLAlchemy
db = SQLAlchemy()

nom_app/models/nom_modèle.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()

J'utilise run.py pour exécuter l'application localement, mais j'utilise nginx + uWSGI pour exécuter l'application dans les environnements dev/stage/live.

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

19
Chris McKinnel

Je pense que la façon la plus simple d'utiliser une base de données existante avec sqlalchemy est d'utiliser la classe AutomapBase . Un exemple de code provenant de documents 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="[email protected]", 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

10
droidmad

J'essaie d'utiliser la génération automatique mais rien ne fonctionne ou je ne pouvais pas l'exécuter. Lorsque je cherche à générer du code à l'aide de sqlacodegen, je trouve https://github.com/ksindi/flask-sqlacodegen , vous pouvez générer le code simplement

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

J'ai essayé et ça marche parfaitement

3
mcolak

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
user5757854

Il s'agit d'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 votre 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
sequoia

alembic (l'outil derrière flask-sqlalchemy) peut être configuré pour ignorer les tables. La configuration n'est pas trop difficile à installer. voir: https://Gist.github.com/utek/616325

0
Adam Greenhall