15

this is my model class

class Type(enum.Enum):
    Certified = "certified"
    Non_Certified = "non-certified"


class Status(enum.Enum):
    Approved = "Approved"
    Rejected = "Rejected"
    Published = "Published"
    Retired = "Retired"
    Waiting_for_Approval = "Waiting_for_Approval"


class DifficultyLevel(enum.Enum):
    Beginner = "Beginner"
    Intermediate = "Intermediate"
    Advanced = "Advanced"
    All = "All"


class ActiveStatus(enum.Enum):
    Archive = "archive"
    Restore = "restore"


class Course(Base):
    __tablename__ = 'course'
    id = Column(Integer, primary_key=True)
    course_code = Column(String(255))
    duration_in_hours = Column(Integer)
    default_eb_price = Column(Integer)
    modified_by = Column(Integer)
    modified_on = Column(DateTime)
    created_by = Column(Integer)
    created_at = Column(DateTime)
    type = Column(Enum(Type))
    certification_vendor = Column(String(255))
    certification_name = Column(String(255))
    micro_training_sessions = Column(Integer)
    status = Column(Enum(Status))
    title = Column(String(255))
    summary = Column(String(255))
    duration = Column(JSON)
    # Categories =
    delivery_language = Column(String(255))
    course_logo = Column(String(255))
    promo_video = Column(String(255))
    overview = Column(String(255))
    objectives = Column(String(255))
    suggested_attendees = Column(String(255))
    prerequisites = Column(String(255))
    difficulty_level = Column(Enum(DifficultyLevel))
    course_facts = Column(JSON)
    price = Column(String(255))
    list_price = Column(String(255))
    early_bird_price = Column(String(255))
    next_recommended_course = Column(postgresql.ARRAY(Integer))
    specialization_paths = Column(postgresql.ARRAY(Integer))
    active_status = Column(Enum(ActiveStatus))

    def __getitem__(self, item):
        return getattr(self, item)

    @property
    def serialize(self):
        """Return object data in easily serializeable format"""
        serialized_obj = {}
        for column in self.__table__.columns:
            serialized_obj[column.key] = self[column.key]
        return serialized_obj

and this the controller function to edit the change

def update_course_ctrl(obj,course_id):
    args = request.args
    course_schema = CourseSchema()
    if args is not None :
        if args['action'] == "archive":
            course = session.query(Course).filter_by(id=course_id).one()
            course.active_status = 'Archive'
            session.merge(course)
            session.commit()
            dump_data = course_schema.dump(course).data
            return dump_data
            # code to archive course
        if args['action'] == "restore":
            course = session.query(Course).filter_by(id=course_id).one()
            course.active_status = 'Restore'
            session.merge(course)
            session.commit()
            dump_data = course_schema.dump(course).data
            return dump_data
    course = Course(**obj.json)
    session.merge(course)
    session.commit()
    dump_data = course_schema.dump(course).data
    return dump_data

this is my code in marshmallow file

from marshmallow_sqlalchemy import ModelSchema
from sqlalchemy.orm import sessionmaker
from app.extensions import engine
from app.course.models import Course, Project, Topic, Resource


DBSession = sessionmaker(bind=engine)
session = DBSession()


class CourseSchema(ModelSchema):
    class Meta:
        model = Course
        sqla_session = session

while calling this update function I am getting this error TypeError: <ActiveStatus.Archive: 'archive'> is not JSON serializable

tcb
  • 2,745
  • 21
  • 20
kartikey
  • 358
  • 1
  • 3
  • 13

5 Answers5

17

For enum fields, the simplest approach is to install the package called marshmallow_enum via pip (pip install marshmallow_enum), importing it in your project and then in your marshmallow schema definitions override the SQLAlchemy fields with your custom definition:

from marshmallow_enum import EnumField
...


class CourseSchema(ModelSchema):
    type = EnumField(Type, by_value=True)

    class Meta:
        model = Course
        sqla_session = session

All fields that weren't overridden by your custom definitions will be taken from your model.

You can experiment with the by_value parameter, which enables you to either serialize names (by default) or values (by_value=True) of your enums.

synweap15
  • 454
  • 5
  • 9
  • 1
    It looks like they added an [Enum field](https://marshmallow.readthedocs.io/en/stable/marshmallow.fields.html#marshmallow.fields.Enum) to Marshmallow [3.18.0](https://github.com/marshmallow-code/marshmallow/blob/dev/CHANGELOG.rst#3180-2022-09-15) (2022-09-15), so the `marshmallow_enum` package will [not be needed anymore](https://github.com/justanr/marshmallow_enum/issues/51) – thaddeusmt Nov 02 '22 at 22:48
8

You can use Marshmallow custom method fields

from marshmallow import fields

class CourseSchema(ModelSchema):
    type = fields.Method("get_course_type")

    def get_course_type(self, obj):
        return obj.type.value

    class Meta:
        model = Course
        sqla_session = session
Espoir Murhabazi
  • 5,973
  • 5
  • 42
  • 73
Srinivas
  • 136
  • 2
  • 5
3

Another alternative is to override the __str__ method of your Enum. E.g.

class Type(enum.Enum):
    Certified = "certified"
    Non_Certified = "non-certified"

    def __str__(self):
        return self.value
clapas
  • 1,768
  • 3
  • 16
  • 29
1

Marshmallow 3.18.0 (2022-09-15) now supports this, via the Enum field type:

import enum
from marshmallow import Schema, fields

class ActiveStatus(enum.Enum):
    Archive = "archive"
    Restore = "restore"

class CalculationSchema(Schema):
    active_status = fields.Enum(ActiveStatus)

(Or something close to that)

thaddeusmt
  • 15,410
  • 9
  • 67
  • 67
0

here is how you can handle enums in marshmallow simply do this with out adding any dependency to the project:

from marshmallow import Schema, fields
from marshmallow.validate import OneOf

Class CourseSchema:
    type = fields.Function(serialize=lambda obj: obj.type.name if 
                              obj.type is not None else None,
                              deserialize=lambda val: val,
                              validate=OneOf(
                                  [e.name for e in Type]),
                              required=False)
    ...

by doing this in the api you only have to pass the enum values(in this case Certified or Non_Certified).