簡體   English   中英

Flask-Restx 未將枚舉字段類型轉換為 JSON

[英]Flask-Restx not converting enum field type to JSON

我需要 Enum 字段類型的幫助,因為它不被 Swagger 接受,並且我收到錯誤消息**TypeError: Object or Type eGameLevel is not JSON serializable** 以下是表格的完整代碼集。 提供了包含 DB 表和 sqlalchemy 設置的完整代碼集。 我已經用 Marshmallow-Enum Flask package 進行了嘗試,但沒有成功。 尋求有關解決方案的一些解釋的幫助,以便我可以很好地學習它。 :-)

我正在使用 MySQL 和 Flask。 在 Postgres 中,管理所有選擇字段非常容易。 我需要的只是一個工作示例或存儲庫鏈接,其中 MySQL 選擇字段顯示在 swagger 下拉列表中。

我的 Model:

import enum
from app import db
from typing import List


class eGameLevel(enum.Enum):
    BEGINNER    =   'Beginner'
    ADVANCED    =   'Advanced'


class Game(Base):
    __tablename__ = 'game_stage'

    id                              = db.Column(db.Integer(), primary_key=True)
    game_level= db.Column(db.Enum(eGameLevel), 
             default=eGameLevel.BEGINNER, nullable=False)
    user_id = db.Column(db.Integer(), db.ForeignKey('users.id', ondelete='CASCADE'), nullable=False)
    user = db.relationship('User', backref='game__level_submissions', lazy=True)

def __init__(self, game_level, user_id):
    self.game_level = game_level
    self.user_id = user_id

def __repr__(self):
    return 'Game(game_level%s, ' \
           'user_id%s'%(self.game_level,
                        self.user_id)
def json(self):
    return {'game_level':self.game_level,
            'user_id':self.user_id}

@classmethod
def by_game_id(cls, _id):
    return cls.query.filter_by(id=_id)

@classmethod
def find_by_game_level(cls, game_level):
    return cls.query.filter_by(game_level=game_level)

@classmethod
def by_user_id(cls, _user_id):
    return cls.query.filter_by(user_id=_user_id)

@classmethod
def find_all(cls) -> List["Game"]:
    return cls.query.all()

def save_to_db(self) -> None:
    db.session.add(self)
    db.session.commit()

def delete_from_db(self) -> None:
    db.session.delete(self)
    db.session.commit()

我的架構

from app import ma
from app.models import Gode

class GameSchema(ma.SQLAlchemyAutoSchema):
    game = ma.Nested('GameSchema', many=True)
    class Meta:
        model =  Game
        load_instance = True
        include_fk= True

我的資源:

from flask_restx import Resource, fields, Namespace
from app.models import Game
from app import db
from app.schemas import GameSchema

GAME_REQUEST_NOT_FOUND = "Game request not found."
GAME_REQUEST_ALREADY_EXSISTS = "Game request '{}' Already exists."

game_ns = Namespace('Game', description='Available Game Requests')
games_ns = Namespace('Game Requests', description='All Games Requests')


game_schema = GameSchema()
games_list_schema = GameSchema(many=True)


gamerequest = game_ns.model('Game', {
    'game_level': fields.String('Game Level: Must be one of: BEGINNER, ADVANCED.'),
    'user_id': fields.Integer,

})


class GameRequestsListAPI(Resource):
    @games_ns.doc('Get all Game requests.')
    def get(self):
        return games_list_schema.dump(Game.find_all()), 200
    @games_ns.expect(gamerequest)
    @games_ns.doc("Create a Game request.")
    def post(self):
        game_json = request.get_json()
        game_data = game_schema.load(game_json)
        game_data.save_to_db()

        return game_schema.dump(game_data), 201

我建議不要嘗試管理 MySQL 模式的枚舉字段,而是使用另一個帶有 backref 的表到您的 eGameLevel。 您可以擺脫這整個大驚小怪,並且將來如果您需要在您的選擇字段中添加另一個選項,您將不必對其進行硬編碼。

只需將主表創建為 Game,將子表創建為 eGameLevel(只有一個字符串字段)。 您將能夠從您的游戲桌訪問選項。

每當我被卡住時,我 go 到 這里提到的基礎知識。

而不是使用枚舉:

class eGameLevel(enum.Enum):
    BEGINNER    =   'Beginner'
    ADVANCED    =   'Advanced'

您可以使用字典:

eGameLevel = {"BEGINNER": 1, "ADVANCED": 2}

然后您可以將 sql 數據 model 的枚舉類型替換為字符串類型:

game_level= db.Column(db.String(), 
             default=eGameLevel["BEGINNER"], nullable=False)

並在整個應用程序中使用定義的字典進行適當的檢查。 這也將解決 alembic 以及進行數據庫遷移的問題。

我無法運行您的示例,似乎缺少某些文件,並且我不清楚您的項目結構。 您可以與所有源代碼和需求文件共享一個存儲庫嗎? 我做了一個小例子只是為了測試一個枚舉的序列化

from enum import Enum

import sqlalchemy as sa
from flask import Flask
from flask_restx import Api, Namespace, Resource
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker


class eGameLevel(str, Enum):
    BEGINNER = "Beginner"
    ADVANCED = "Advanced"


engine = sa.create_engine("sqlite:///:memory:")
session = scoped_session(sessionmaker(bind=engine))
Base = declarative_base()


class Game(Base):
    __tablename__ = "game"
    id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
    level = sa.Column(sa.Enum(eGameLevel), default=eGameLevel.BEGINNER, nullable=False)

    def __repr__(self):
        return f"Game(id={self.id}, level={self.level})"

    def json(self):
        data = {"id": self.id, "level": self.level}
        return data


Base.metadata.create_all(engine)

g1 = Game(level=eGameLevel.BEGINNER)
g2 = Game(level=eGameLevel.ADVANCED)

session.add_all([g1, g2])
session.commit()
query_content = session.query(Game).all()

games_ns = Namespace("Game Requests", description="All Games Requests")


app = Flask(__name__)
api = Api(app)


@api.route("/game")
class GameRequestsListAPI(Resource):
    @games_ns.doc("Get all Game requests.")
    def get(self):
        data = [x.json() for x in query_content]
        return data, 200


app.run(debug=True)

此示例有效,我認為由於 Enum 聲明中的str ,序列化是可能的: class eGameLevel(str, Enum)

如果您共享整個源代碼,我可以嘗試修復它。

暫無
暫無

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

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