I am building flask project for my learning now, now I have simple schema for Employees like this (Didn't added all column names):
class GenderChoices(enum.Enum):
M = 'M'
F = 'F'
def __str__(self):
return self.name
@staticmethod
def from_string(s):
try:
return GenderChoices[s]
except KeyError:
raise ValueError()
class Employees(db.Model):
___tablename___ = "employees"
emp_no = db.Column(db.Integer, primary_key=True)
gender = db.Column(db.Enum(GenderChoices, values_callable=lambda x: [str(member.value)
for member in GenderChoices]))
Basic Arguments on resources side, I am passing like that:
parser.add_argument('gender',
type=GenderChoices.from_string,
help='This field cannot be blank',
required=True,
choices=list(GenderChoices))
As I am showing my response like this:
return {
'message': 'New Employee Has been created',
'Employee_Details': {
'employee_id': new_id,
'gender': data['gender']
}
}, 201
While running my script to add Employees runs fine and it stores data on my database as well, but on showing my result on postman, I am getting following error:
TypeError: Object of type GenderChoices is not JSON serializable
What seems to be problem.
If you are using Flask-RESTful, you can force the conversion to a string by using the @marshal_with
decorator and specifying a field.String
.
from flask import (
Flask,
render_template,
request
)
from flask_restful import (
Api,
Resource,
fields,
marshal_with,
reqparse
)
from flask_sqlalchemy import SQLAlchemy
import enum
class GenderChoices(enum.Enum):
M = 'M'
F = 'F'
def __str__(self):
return self.name
@staticmethod
def from_string(s):
try:
return GenderChoices[s]
except KeyError:
raise ValueError()
app = Flask(__name__)
app.config.from_mapping(
SECRET_KEY='your secret here',
SQLALCHEMY_DATABASE_URI='sqlite:///demo.db',
)
db = SQLAlchemy(app)
api = Api(app)
parser = reqparse.RequestParser()
parser.add_argument('gender',
type=GenderChoices.from_string,
help='This field cannot be blank',
required=True,
choices=list(GenderChoices)
)
class Employee(db.Model):
__tablename__ = "employees"
id = db.Column(db.Integer, primary_key=True)
gender = db.Column(db.Enum(GenderChoices, values_callable=lambda x: [str(member.value) for member in GenderChoices]))
with app.app_context():
db.drop_all()
db.create_all()
class EmployeeList(Resource):
@marshal_with({
'message': fields.String,
'Employee_Details': fields.Nested({
'employee_id': fields.Integer,
'gender': fields.String
})
})
def post(self):
args = parser.parse_args()
employee = Employee(**args)
db.session.add(employee)
db.session.commit()
return {
'message': 'New Employee Has been created',
'Employee_Details': {
'employee_id': employee.id,
'gender': employee.gender
}
}, 201
api.add_resource(EmployeeList, '/employees')
When using pure flask you can use the following variants.
On the one hand, you can use a StrEnum
whose values can be converted to JSON without any action.
class GenderChoices(enum.StrEnum):
M = 'M'
F = 'F'
If you want to use an alternative method, you can create a custom JSONProvider
and use that in your application. The following example extends the DefaultJSONProvider
.
from flask.json.provider import DefaultJSONProvider
class CustomJSONProvider(DefaultJSONProvider):
@staticmethod
def default(o):
if isinstance(o, enum.Enum):
return o.name
return DefaultJSONProvider.default(o)
app = Flask(__name__)
app.json_provider_class = CustomJSONProvider
app.json = CustomJSONProvider(app)