Intro
Я прочитал около тысячи постов на SO и других сайтах, пытаясь выяснить, что не так с моей структурой Flask и почему я не могу что-то выяснить. В качестве последнего средства я решил наконец задать вопрос здесь.
Мой проект действительно прост:
- Мне нужно получить некоторые данные с некоторых сетевых устройств через API, обработать данные и сохраните их в Postgresql БД (большая часть кода находится в
lib/
). - Этот проект будет развернут в нескольких средах (test, dev, staging и prod).
Для этого я использую следующее:
Детали проекта
Структура моего проекта выглядит следующим образом:
my_project/
├── api/
├── app.py
├── config.py
├── __init__.py
├── lib/
│ ├── exceptions.py
│ └── f5_bigip.py
├── log.py
├── logs/
├── manage.py
├── migrations/
├── models/
│ ├── __init__.py
│ ├── model1.py
│ └── model2.py
└── run.py
My app.py выглядит так:
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from dotenv import load_dotenv
from flask import Flask
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
migrate = Migrate()
def create_app():
load_dotenv()
app = Flask(__name__)
environment = app.config['ENV']
if environment == 'production':
app.config.from_object('config.ProductionConfig')
elif environment == 'testing':
app.config.from_object('config.TestingConfig')
else:
app.config.from_object('config.DevelopmentConfig')
db.init_app(app)
migrate.init_app(app, db)
return app
Мой config.py выглядит так:
import os
from sqlalchemy.engine.url import URL
PROJECT_ROOT = os.path.dirname(os.path.realpath(__file__))
class BaseConfig:
DEBUG = False
TESTING = False
DB_DRIVERNAME = os.getenv('DB_DRIVERNAME')
DB_HOST = os.getenv('DB_HOST')
DB_PORT = os.getenv('DB_PORT')
DB_NAME = os.getenv('DB_NAME')
DB_USERNAME = os.getenv('DB_USERNAME')
DB_PASSWORD = os.getenv('DB_PASSWORD')
DB = {
'drivername': DB_DRIVERNAME,
'host': DB_HOST,
'port': DB_PORT,
'database': DB_NAME,
'username': DB_USERNAME,
'password': DB_PASSWORD,
}
SQLALCHEMY_DATABASE_URI = URL(**DB)
SQLALCHEMY_TRACK_MODIFICATIONS = False
class DevelopmentConfig(BaseConfig):
DEVELOPMENT = True
DEBUG = True
class TestingConfig(BaseConfig):
TESTING = True
class StagingConfig(BaseConfig):
DEVELOPMENT = True
DEBUG = True
class ProductionConfig(BaseConfig):
pass
Мой __ init__.py выглядит так:
from contextlib import contextmanager
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
# here, create_engine needs the SQLALCHEMY_DATABASE_URI
# how do I get it from the proper config?
engine = create_engine()
Session = sessionmaker(bind=engine)
@contextmanager
def session_scope():
"""
Provide a transactional scope around a series of operations.
"""
session = Session()
try:
yield session
session.commit()
except Exception as e:
print(f'Something went wrong here: {str(e)}. rolling back.')
session.rollback()
raise
finally:
session.close()
My manage.py выглядит так:
from flask_script import Manager
from flask_migrate import MigrateCommand
from app import create_app
from models import *
manager = Manager(create_app)
manager.add_command('db', MigrateCommand)
if __name__ == '__main__':
manager.run()
My models / model1.py выглядит так:
from sqlalchemy.dialects.postgresql import INET
from sqlalchemy.sql import func
from app import db
class Model1(db.Model):
__tablename__ = 'model1'
id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
ip_address = db.Column(INET, unique=True, nullable=False)
last_update = db.Column(db.DateTime(), server_default=func.now())
def __repr__(self):
return f'<Model1: {self.ip_address}>'
def __init__(self, ip_address):
self.ip_address = ip_address
Вопросы
Теперь у меня есть три основных вопроса:
- В моем основном
__init__.py
Как я могу импорт SQLALCHEMY_DATABASE_URI
из конфигурации приложения? - Наличие объекта
Session()
в __init__.py
не кажется слишком интуитивным. Должен ли он быть размещен в других местах? Для большего контекста session
используется в lib/f5_bigip.py
и, вероятно, будет использоваться также в api/
. - В целом структура проекта в порядке?