Masalah impor / konteks Flask-SQLAlchemy

117

Saya ingin menyusun aplikasi Flask saya seperti:

./site.py
./apps/members/__init__.py
./apps/members/models.py

apps.members adalah Cetak Biru Flask.

Sekarang, untuk membuat kelas model saya perlu memiliki aplikasinya, seperti:

# apps.members.models
from flask import current_app
from flaskext.sqlalchemy import SQLAlchemy

db = SQLAlchemy(current_app)

class Member(db.Model):
    # fields here
    pass

Tetapi jika saya mencoba dan mengimpor model itu ke dalam aplikasi Blueprint saya, saya merasa ditakuti RuntimeError: working outside of request context. Bagaimana saya bisa mendapatkan aplikasi saya dengan benar di sini? Impor relatif mungkin berfungsi tetapi sangat buruk dan memiliki masalah konteksnya sendiri, misalnya:

from ...site import app

# ValueError: Attempted relative import beyond toplevel package
Brad Wright
sumber

Jawaban:

295

The flask_sqlalchemymodul tidak harus diinisialisasi dengan aplikasi segera - Anda dapat melakukan ini sebagai gantinya:

# apps.members.models
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class Member(db.Model):
    # fields here
    pass

Dan kemudian dalam pengaturan aplikasi Anda, Anda dapat memanggil init_app:

# apps.application.py
from flask import Flask
from apps.members.models import db

app = Flask(__name__)
# later on
db.init_app(app)

Dengan cara ini Anda dapat menghindari impor siklis.

Pola ini tidak mengharuskan Anda menempatkan semua model Anda dalam satu file. Cukup impor dbvariabel ke setiap modul model Anda.

Contoh

# apps.shared.models
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

# apps.members.models
from apps.shared.models import db

class Member(db.Model):
    # TODO: Implement this.
    pass

# apps.reporting.members
from flask import render_template
from apps.members.models import Member

def report_on_members():
    # TODO: Actually use arguments
    members = Member.filter(1==1).all()
    return render_template("report.html", members=members)

# apps.reporting.routes
from flask import Blueprint
from apps.reporting.members import report_on_members

reporting = Blueprint("reporting", __name__)

reporting.route("/member-report", methods=["GET","POST"])(report_on_members)

# apps.application
from flask import Flask
from apps.shared import db
from apps.reporting.routes import reporting

app = Flask(__name__)
db.init_app(app)
app.register_blueprint(reporting)

Catatan: ini adalah sketsa dari beberapa kekuatan yang diberikan ini - jelas ada sedikit lebih banyak yang dapat Anda lakukan untuk membuat pengembangan lebih mudah (menggunakan create_apppola, cetak biru pendaftaran otomatis di folder tertentu, dll.)

Sean Vieira
sumber
2
Bisakah Anda melakukannya berkali-kali? Misalnya jika saya memiliki lebih dari satu file models.py?
Brad Wright
@BradWright - membuatnya lebih mudah jika Anda hanya membuat dbinstance untuk setiap database yang Anda miliki. Jika Anda memiliki paket model, Anda dapat memasukkannya ke dalamnya __init__.py. Bagaimanapun Anda memilih untuk melakukannya, Anda cukup mengimpor dbvariabel dari lokasi itu ke file model Anda yang lain dan menggunakannya seperti biasa. Ketika mereka dimuat semuanya diselesaikan dengan benar.
Sean Vieira
1
Apakah Anda kebetulan memiliki tautan ke proyek yang disiapkan dengan cara ini?
Mbrevda
4
@Mbrevda - Anda dapat melihat contoh pola ini di sini github.com/svieira/Budget-Manager
Sean Vieira
1
The .ext.namespace sudah ditinggalkan - lebih baik untuk impor dari namespace nyata ( flask_sqlalchemy).
Sean Vieira
25

app.py asli : https://flask-sqlalchemy.palletsprojects.com/en/2.x/quickstart/

...

app = flask.Flask(__name__)
app.config['DEBUG'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = flask.ext.sqlalchemy.SQLAlchemy(app)

class Person(db.Model):
    id = db.Column(db.Integer, primary_key=True)
...

class Computer(db.Model):
    id = db.Column(db.Integer, primary_key=True)
...

# Create the database tables.
db.create_all()

...

# start the flask loop
app.run()

Saya baru saja memisahkan satu app.py ke app.py dan model.py tanpa menggunakan Blueprint. Dalam hal ini, jawaban dosen tidak berfungsi. Kode baris diperlukan untuk bekerja.

sebelumnya :

db.init_app(app)

setelah :

db.app = app
db.init_app(app)

Dan, tautan berikut sangat berguna.

http://piotr.banaszkiewicz.org/blog/2012/06/29/flask-sqlalchemy-init_app/

cybaek
sumber
2
db.app = appmendapatkan runtimeerror karena init_app tidak menyetel aplikasi. +1
Amit Tripathi
3
db.app = app(diikuti oleh db.init_app(app)) adalah bagian yang hilang bagi saya. Bekerja dengan sempurna setelah menambahkan garis itu (dikombinasikan dengan jawaban Sean Vieira)
Dotl
Tautan pertama sudah usang.
Rahul KP