簡體   English   中英

使用 DB 數據模型生成 SQLAlchemy 模型、模式和 JSON 響應

[英]Use DB data model to generate SQLAlchemy models, schemas, and JSON response

將 Flask 和 SQLAlchemy 用於 Python 網絡應用程序,我的目標是創建一個系統,我可以在其中:

  1. 從現有的 PostgreSQL 數據庫導入數據模型,並將它們映射到相應 SQLAlchemy 模型中的字段
  2. 使用這些 SQLAlchemy 模型自動生成模式。 然后,此架構將用於對用戶提交的數據執行數據驗證。(我目前正在嘗試使用 Marshmallow,但我願意接受其他建議)。
  3. 使用步驟 2 中生成的架構執行 JSON 響應格式化。(我目前正在嘗試根據 JsonAPI 的架構格式化我的響應 - 如果需要,這可能會改變,但我更喜歡它)。

所有這些都將打包在一個層中,在編寫 API 時應該允許簡單的數據訪問、驗證和響應格式化,希望無需手動定義數據模型或模式,而不是超越數據庫中已經存在的定義。 這引出了我的問題:

我可以利用現有的框架來完成我想要完成的所有事情嗎? 我不希望有一個庫可以做所有事情,但我希望能夠利用幾個現有的框架。 但是,我遇到了直接沖突,尤其是在第 2 步和第 3 步中。到目前為止,我一直在嘗試使用的堆棧如下:

  • Flask(我的網絡框架)
  • SQLAlchemy(用於通過反射訪問我的數據,見下文)
  • Flask-Marshmallow(生成數據驗證模式)
  • Marshmallow-JsonAPI(根據 JsonAPI 規范格式化 JSON 響應)

我有第 1 步的解決方案:SQLAlchemy 的反射。 這允許我讀取現有數據庫中的表並將它們映射到 SQLAlchemy 模型。 這很好用。

第 2 步和第 3 步的組合變得模糊 - 我正在嘗試使用 Flask-Marshmallow 和 Marshmallow-JsonAPI。 Flask-Marshmallow 具有ModelSchema 類,它允許您通過將現有的 SQLAlchemy 模型傳遞給它來生成模式。 我可以將我在第 1 步中生成的 SQLAlchemy 模型傳遞給它,滿足我在第 2 步中的標准。至於 Marshmallow-JsonAPI,它具有可以為模型定義架構的功能,並且它會自動創建符合 JSONAPI 的響應,滿足我的第 3 步標准。我可以單獨使用這些框架中的每一個來執行我需要的操作。

不幸的是,由於我的架構必須從 Flask-Marshmallow 和 Marshmallow-JSONAPI 中的 Schema 類繼承,我遇到了問題。 我的“虛擬”類模型如下所示:

import flask
from marshmallow_jsonapi import Schema, fields
from sqlalchemy.ext.declarative import DeferredReflection
from flask_marshmallow import Marshmallow
from flask_sqlalchemy import SQLAlchemy

app = flask.Flask(__name__)
DB = SQLAlchemy()
DB.init_app(app)
MA = Marshmallow(app)
DeferredReflection.prepare(DB.get_engine(app))

class Dummy(DeferredReflection, DB.Model):
    __tablename__ = "dummy"  # This model will be reflected from the Dummy table


class DummySchema(MA.ModelSchema, Schema):  # The problem line
    class Meta:
        model = Dummy  # Create schema for the Dummy model

在嘗試啟動我的 API 時,我收到如下奇怪的錯誤:

api_1        | [2017-01-05 23:16:13,379] ERROR in app: Exception on /api/dummy [POST]
api_1        | Traceback (most recent call last):
api_1        |   File "/usr/local/lib/python3.5/site-packages/flask/app.py", line 1612, in full_dispatch_request
api_1        |     rv = self.dispatch_request()
api_1        |   File "/usr/local/lib/python3.5/site-packages/flask/app.py", line 1598, in dispatch_request
api_1        |     return self.view_functions[rule.endpoint](**req.view_args)
api_1        |   File "/usr/local/lib/python3.5/site-packages/flask_restful/__init__.py", line 477, in wrapper
api_1        |     resp = resource(*args, **kwargs)
api_1        |   File "/usr/local/lib/python3.5/site-packages/flask/views.py", line 84, in view
api_1        |     return self.dispatch_request(*args, **kwargs)
api_1        |   File "/usr/local/lib/python3.5/site-packages/flask_restful/__init__.py", line 587, in dispatch_request
api_1        |     resp = meth(*args, **kwargs)
api_1        |   File "/dummyproj/api/src/dummyproj/api/v1/dummy_resource.py", line 34, in post
api_1        |     dummy_schema = dummy.DummySchema()
api_1        |   File "/usr/local/lib/python3.5/site-packages/marshmallow_sqlalchemy/schema.py", line 143, in __init__
api_1        |     super(ModelSchema, self).__init__(*args, **kwargs)
api_1        |   File "/usr/local/lib/python3.5/site-packages/marshmallow_jsonapi/schema.py", line 81, in __init__
api_1        |     super(Schema, self).__init__(*args, **kwargs)
api_1        |   File "/usr/local/lib/python3.5/site-packages/marshmallow/schema.py", line 358, in __init__
api_1        |     self._update_fields(many=many)
api_1        |   File "/usr/local/lib/python3.5/site-packages/marshmallow/schema.py", line 750, in _update_fields
api_1        |     self.__set_field_attrs(ret)
api_1        |   File "/usr/local/lib/python3.5/site-packages/marshmallow/schema.py", line 772, in __set_field_attrs
api_1        |     self.on_bind_field(field_name, field_obj)
api_1        |   File "/usr/local/lib/python3.5/site-packages/marshmallow_jsonapi/schema.py", line 164, in on_bind_field
api_1        |     field_obj.load_from = self.inflect(field_name)
api_1        |   File "/usr/local/lib/python3.5/site-packages/marshmallow_jsonapi/schema.py", line 190, in inflect
api_1        |     return self.opts.inflect(text) if self.opts.inflect else text
api_1        | AttributeError: 'SchemaOpts' object has no attribute 'inflect'

似乎由於我的 Schema 類繼承自兩個不同的超類,因此會導致問題。 我希望這兩個棉花糖庫非常兼容,但似乎確實存在問題

所有這一切的結果是,我不太確定接下來要嘗試什么。 我並不是真的期待任何關於 Marshmallow 框架的具體建議,更多只是想展示我的思路。 是否有圍繞這種設計的最佳實踐,或者我是否試圖一次解決太多問題?

(Python 和 SO 的新手 - 如果其中任何一個不清楚,請道歉)。

我成功地結合了 marshmallow-jsonapi + marshmallow-sqlalchemy。 這是魔法:

import marshmallow
import marshmallow_jsonapi
import marshmallow_jsonapi.flask
import marshmallow_sqlalchemy

import myapp.database as db


def make_jsonapi_schema_class(model_class):
    class SchemaOpts(marshmallow_sqlalchemy.SQLAlchemyAutoSchemaOpts, marshmallow_jsonapi.SchemaOpts):
        pass

    class Schema(marshmallow_sqlalchemy.SQLAlchemyAutoSchema, marshmallow_jsonapi.flask.Schema):
        OPTIONS_CLASS = SchemaOpts

        @marshmallow.post_load
        def make_instance(self, data, **kwargs):
            # Return deserialized data as a dict, not a model instance
            return data

        # You can add default behavior here, for example
        # id = fields.Str(dump_only=True)

    # https://marshmallow-sqlalchemy.readthedocs.io/en/latest/recipes.html#automatically-generating-schemas-for-sqlalchemy-models
    class Meta:
        # Marshmallow-SQLAlchemy
        model = model_class
        sqla_session = db.session

        # Marshmallow-JSONAPI
        type_ = model_class.__name__.lower()
        self_view = type_ + '_detail'
        self_view_kwargs = {'id': '<id>'}
        self_view_many = type_ + '_list'

    schema_class = type(model_class.__name__ + 'Schema', (Schema,), {'Meta': Meta})
    return schema_class

然后你打電話

FooSchema = make_jsonapi_schema_class(Foo)

從您的 SQLAlchemy 聲明模型生成棉花糖模式類。 如果您想自定義它,您可以反過來繼承該類。

(為了實現 REST API,我使用了Flask-REST-JSONAPI ,它建立在 Flask + SQLAlchemy + marshmallow-jsonapi 之上。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM