ปัญหาการนำเข้า / บริบทของ Flask-SQLAlchemy


117

ฉันต้องการจัดโครงสร้างแอป Flask ของฉันเช่น:

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

apps.members คือ Flask Blueprint

ตอนนี้ในการสร้างคลาสโมเดลฉันต้องมีแอพค้างไว้บางอย่างเช่น:

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

db = SQLAlchemy(current_app)

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

แต่ถ้าฉันลองและนำเข้าโมเดลนั้นไปยังแอพพิมพ์เขียวของฉันฉันก็รู้สึกหวั่นRuntimeError: working outside of request contextๆ ฉันจะระงับแอปของฉันอย่างถูกต้องที่นี่ได้อย่างไร การนำเข้าสัมพัทธ์อาจใช้งานได้ แต่ค่อนข้างน่าเกลียดและมีปัญหาบริบทของตัวเองเช่น:

from ...site import app

# ValueError: Attempted relative import beyond toplevel package

คำตอบ:


295

flask_sqlalchemyโมดูลไม่ได้จะต้องมีการเริ่มต้นกับ app ทันที - คุณสามารถทำเช่นนี้แทน:

# apps.members.models
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

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

จากนั้นในการตั้งค่าแอปพลิเคชันของคุณคุณสามารถโทรinit_app:

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

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

ด้วยวิธีนี้คุณสามารถหลีกเลี่ยงการนำเข้าที่เป็นวัฏจักรได้

รูปแบบนี้ไม่ได้ทำให้คุณต้องวางโมเดลทั้งหมดของคุณไว้ในไฟล์เดียว เพียงแค่นำเข้าdbตัวแปรไปยังโมดูลโมเดลแต่ละโมดูลของคุณ

ตัวอย่าง

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

หมายเหตุ:นี่เป็นภาพร่างของพลังบางอย่างที่ให้คุณ - เห็นได้ชัดว่ามีอีกเล็กน้อยที่คุณสามารถทำได้เพื่อให้การพัฒนาง่ายยิ่งขึ้น (ใช้create_appรูปแบบการลงทะเบียนพิมพ์เขียวอัตโนมัติในบางโฟลเดอร์เป็นต้น)


2
คุณสามารถทำหลาย ๆ ครั้งได้หรือไม่? ตัวอย่างเช่นหากฉันมีไฟล์ models.py มากกว่าหนึ่งไฟล์?
Brad Wright

@BradWright - มันทำให้ง่ายขึ้นถ้าคุณสร้างเฉพาะdbอินสแตนซ์สำหรับแต่ละฐานข้อมูลที่คุณมี หากคุณมีแพ็คเกจของโมเดลคุณสามารถใส่ลงไป__init__.pyได้ อย่างไรก็ตามคุณเลือกที่จะทำเพียงแค่นำเข้าdbตัวแปรจากตำแหน่งนั้นไปยังไฟล์โมเดลอื่นของคุณและใช้งานได้ตามปกติ เมื่อโหลดแล้วทุกอย่างจะแก้ไขได้อย่างถูกต้อง
Sean Vieira

1
คุณจะมีลิงก์ไปยังโครงการที่ตั้งค่าด้วยวิธีนี้หรือไม่?
มิย่อด้า

4
คุณสามารถดูตัวอย่างรูปแบบนี้ได้ที่github.com/svieira/Budget-Manager
Sean Vieira

1
.ext.namespace เลิก - ดีกว่าที่จะนำเข้าจาก namespace จริง ( flask_sqlalchemy)
Sean Vieira

25

app.pyดั้งเดิม: 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()

ฉันเพิ่งแยก app.py หนึ่งรายการไปยัง app.py และ model.py โดยไม่ต้องใช้ Blueprint ในกรณีนี้คำตอบข้างต้นไม่ได้ผล ต้องใช้รหัสบรรทัดในการทำงาน

ก่อน :

db.init_app(app)

หลังจาก :

db.app = app
db.init_app(app)

และลิงค์ต่อไปนี้มีประโยชน์มาก

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


2
db.app = appได้รับ runtimeerror เนื่องจาก init_app ไม่ได้ตั้งค่าแอป +1
Amit Tripathi

3
db.app = app(ตามด้วยdb.init_app(app)) คือชิ้นส่วนที่ขาดหายไปสำหรับฉัน ทำงานได้อย่างสมบูรณ์หลังจากเพิ่มบรรทัดนั้น (รวมกับคำตอบของ Sean Vieira)
Dotl

ลิงก์แรกล้าสมัย
Rahul KP
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.