Sự cố ngữ cảnh / nhập Flask-SQLAlchemy


117

Tôi muốn cấu trúc ứng dụng Flask của mình như sau:

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

apps.members là một Bản thiết kế bình.

Bây giờ, để tạo các lớp mô hình, tôi cần phải có ứng dụng, giống như:

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

db = SQLAlchemy(current_app)

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

Nhưng nếu tôi thử và nhập mô hình đó vào ứng dụng Blueprint của mình, tôi sẽ rất sợ RuntimeError: working outside of request context. Làm cách nào tôi có thể nắm giữ ứng dụng của mình một cách chính xác tại đây? Nhập tương đối có thể hoạt động nhưng chúng khá xấu và có các vấn đề ngữ cảnh riêng, ví dụ:

from ...site import app

# ValueError: Attempted relative import beyond toplevel package

Câu trả lời:


295

Các flask_sqlalchemymô-đun không cần phải được khởi tạo với các ứng dụng ngay lập tức - bạn có thể làm điều này thay vì:

# apps.members.models
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

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

Và sau đó trong thiết lập ứng dụng của mình, bạn có thể gọi init_app:

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

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

Bằng cách này, bạn có thể tránh nhập hàng theo chu kỳ.

Mẫu này không bắt buộc bạn phải đặt tất cả các mẫu của mình trong một tệp. Chỉ cần nhập dbbiến vào từng mô-đun mô hình của bạn.

Thí dụ

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

Lưu ý: đây là bản phác thảo về một số sức mạnh mà điều này mang lại cho bạn - rõ ràng là bạn có thể làm được nhiều việc hơn nữa để phát triển dễ dàng hơn (sử dụng create_appmẫu, tự động đăng ký bản thiết kế trong một số thư mục nhất định, v.v.)


2
Bạn có thể làm điều đó nhiều lần không? Ví dụ: nếu tôi có nhiều tệp models.py?
Brad Wright

@BradWright - sẽ dễ dàng hơn nếu bạn chỉ tạo dbphiên bản cho mỗi cơ sở dữ liệu mà bạn có. Nếu bạn có một gói mô hình, bạn có thể đặt nó vào __init__.py. Tuy nhiên bạn chọn làm điều đó, bạn chỉ cần nhập dbbiến từ vị trí đó vào các tệp mô hình khác của mình và sử dụng nó như bình thường. Khi chúng được tải, mọi thứ sẽ giải quyết chính xác.
Sean Vieira

1
Bạn có tình cờ có một liên kết đến một dự án được thiết lập theo cách này không?
Mbrevda

4
@Mbrevda - bạn có thể xem ví dụ về mẫu này tại đây github.com/svieira/Budget-Manager
Sean Vieira

1
Không .ext.gian tên không được dùng nữa - tốt hơn là nhập từ không gian tên thực ( flask_sqlalchemy).
Sean Vieira

25

một app.py gốc : 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()

Tôi vừa tách một app.py thành app.py và model.py mà không sử dụng Blueprint. Trong trường hợp đó, câu trả lời trên không hoạt động. Một mã dòng là cần thiết để hoạt động.

trước :

db.init_app(app)

sau :

db.app = app
db.init_app(app)

Và, liên kết sau đây rất hữu ích.

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


2
db.app = appgặp lỗi thời gian chạy vì init_app không đặt ứng dụng. +1
Amit Tripathi

3
db.app = app(tiếp theo db.init_app(app)) là mảnh ghép còn thiếu đối với tôi. Hoạt động hoàn hảo sau khi thêm dòng đó (kết hợp với câu trả lời của Sean Vieira)
Dotl

Liên kết đầu tiên đã lỗi thời.
Rahul KP
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.