我正在使用应用程序工厂模式,并且我有一个与 M2M 关系中的类别相关的 Article 对象。到目前为止,API 中的所有路由都按预期工作。我可以通过POST MethodViews创建带有类别的文章。
但是,我正在尝试通过单击单独的文件将一些示例数据播种到我的数据库中。起初,我认为问题出在 Flask CLI 和应用程序上下文上,我最初在蓝图中拥有这些上下文,但后来我意识到问题更深了。我看到了这个问题,但我已将我的棉花糖/烧瓶-棉花糖/棉花糖-Sqlalchemy 更新为最新版本:
https://github.com/marshmallow-code/marshmallow-sqlalchemy/issues/20#issuecomment-136400602
model.py
class CRUDMixin():
@classmethod
def find_by_id(cls, _id):
return cls.query.filter_by(id=_id).first()
@classmethod
def find_all(cls):
return cls.query.all()
def save_to_db(self):
db.session.add(self)
return db.session.commit()
def update(self):
return db.session.commit()
def delete_from_db(self):
db.session.delete(self)
return db.session.commit()
class CategoryModel(db.Model, CRUDMixin):
__tablename__ = "api_category"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), nullable=False)
def __repr__(self):
return '<id {}>'.format(self.id)
class ArticleModel(db.Model, CRUDMixin):
__tablename__ = 'api_article'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), nullable=False)
description = db.Column(db.Text)
categories = db.relationship('CategoryModel', secondary=api_category_article, lazy='subquery',
backref=db.backref('articles', lazy=True))
def __repr__(self):
return '<id {}>'.format(self.id)
schema.py
class CategoryPostSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = CategoryModel
dump_only = ("name", )
load_only = ("articles", )
load_instance = True
class ArticlePostSchema(ma.SQLAlchemyAutoSchema):
categories = ma.Nested(CategoryPostSchema, many=True)
class Meta:
model = ArticleModel
dump_only = ("id",)
include_fk = True
load_instance = True
include_relationships = True
sqla_session = db.session
resource.py
class ArticleListView(MethodView):
def __init__(self):
pass
@classmethod
def get(cls):
data = ArticleModel.find_all()
return jsonify({"data": article_list_schema.dump(data),
"count": len(data),
"status": 200
})
@classmethod
def post(cls):
req_json = request.get_json()
errors = article_post_schema.validate(req_json)
if errors:
response = jsonify({'errors': errors, "status": 400})
response.status_code = 400
return response
data = article_post_schema.load(req_json)
data.save_to_db()
response = jsonify({"data": article_post_schema.dump(data), "errors": {}, "status": 201})
response.status_code = 201
return response
initialize.py(在根目录中(
import click
from marshmallow import ValidationError
from api import create_app
from api.db import db
from api.schemas import ArticlePostSchema
app = create_app()
@click.group()
def cli():
pass
@cli.command()
def article():
with app.app_context():
article_post_schema = ArticlePostSchema()
entry = {"name":"My Family Vacation 5",
"description":"That time we took a road trip to Europe",
"categories":[{"id": 1}]
}
data = article_post_schema.load(entry, session=db.session)
data.save_to_db()
print("Success")
if __name__ == '__main__':
cli()
//错误
Traceback (most recent call last):
File "initialize.py", line 41, in <module>
cli()
...
File "initialize.py", line 29, in article
data = article_post_schema.validate(entry)
...
return self.session.query(self.opts.model).filter_by(**filters).first()
AttributeError: 'DummySession' object has no attribute 'query'
好吧!我想我想通了。我最终在 Flask 1( 应用程序上下文和 2( 请求上下文中遇到了两个问题。这个问题与棉花糖模式无关,但我会说,没有 M2M 关系的对象工作是非常令人困惑的,但对于 M2M 关系,它需要一个请求上下文。如果有人愿意解释,那就太好了。
在此过程中,我遇到了以下错误:
AttributeError: 'DummySession' object has no attribute 'query'
RuntimeError: No application found. Either work inside a view function or push an application context.
AssertionError: Popped wrong app context.
我基本上需要将app.app_context()
添加到我的__init__.py
文件中,并在initialize.py
文件中with current_app.test_request_context('/path/to/route/')
和@with_appcontext
。我还需要将函数导入我的__init__.py
。为了保持整洁,我将文件移动到我的应用程序文件夹中
初始化.py
def create_app():
app = Flask(__name__)
# Config
...
# Initialize DB and Marshmallow
db.init_app(app)
ma.init_app(app)
with app.app_context():
# Import Models
...
# Import MethodViews
...
# Import Blueprints
...
# Commands
from api.initialize import article
app.cli.add_command(article)
return app
initialize.py(现在在/api 目录中(
@click.command()
@with_appcontext
def article():
article_post_schema = ArticlePostSchema()
entry = {"name":"My Family Vacation 5",
"description":"That time we took a road trip to Europe",
"categories":[{"id": 1}]
}
with current_app.test_request_context('/article/'):
try:
data = article_post_schema.load(entry)
data.save_to_db()
print("Success")
except ValidationError as err:
print("Errors...", err)
现在运行flask article
就可以了。
注意:在Flask-Click上有许多示例,使用单页文件并在create_app()
中定义命令。这对于简单的示例或阐明基本思想或功能非常有用。但是,这也非常令人困惑,因为似乎任何生产环境中的大多数人都将使用蓝图和应用程序工厂模式作为默认模式。唯一有用的test_request_context
提及是在测试烧瓶应用中。
所以澄清一下,如果你把所有东西都放进你的create_app
你需要在你的__init__.py
中使用with app.test_request_context('/path/to/route/')
。否则,如上例所示,如果将其分离到其他文件中,则需要使用current_app
。
我发现这篇文章对于解释应用程序上下文以及有关此问题的文档缺乏清晰度非常有用:
https://hackingandslacking.com/demystifying-flasks-application-context-c7bd31a53817