python-3.xflaskflask-restful

Getting null responses while using marshal_with for getting data


So, I am working on my self learning on flask, in order to one my problem with enums, I am using marshal_with, which one of user suggested me.. Now I have problem getting data for invalid ids..

I am using following to get response:

def json(self):
    return {
        'data': {
            'employee_id': self.emp_no,
            'employee_name': self.first_name + " " + self.last_name,
            'gender': self.gender,
            'birth_date': self.birth_date,
            'hire_date': self.hire_date,
            'created_at': self.created_at.strftime("%Y-%m-%d %H:%M:%S"),
            'last_updated_at': self.last_updated_at.strftime("%Y-%m-%d %H:%M:%S")
        }
    }

@classmethod
def find_by_employee_id(cls, employee_id):
    return cls.query.filter_by(emp_no=employee_id).first()

at resources section of employees, I have implemented following logic to get results for end point:

class GetEmployee(Resource):
    @marshal_with({
        'data': fields.Nested({
            'employee_id': fields.Integer,
            'employee_name': fields.String,
            'gender': fields.String,
            'birth_date': fields.String,
            'hire_date': fields.String,
            'created_at': fields.Raw,
            'last_updated_at': fields.Raw
        })
    })
    def get(self, employee_id: int):
        employee = Employees.find_by_employee_id(employee_id)
        if not employee:
            return {'message': 'Employee Not Found'}, 404

        return employee.json(), 200

Now good thing is, I am getting response perfectly fine, if I am entering valid employee_id within end point, but if I add invalid id like 4,5, I am getting response like this:

{
    "data": {
        "employee_id": 0,
        "employee_name": null,
        "gender": null,
        "birth_date": null,
        "hire_date": null,
        "created_at": null,
        "last_updated_at": null
    }
}

what I am expecting is the response, which I have added within GetEmployee class, i.e.

{'message': 'Employee Not Found'}

Where is the problem.


Solution

  • There is the possibility of throwing a 404 error when querying the database if no entry is found. It is also possible to define an error message that is output when abort is called automatically following the error. The explanation can be found in the Flask-SQLAlchemy documentation in the "Queries for Views" section.

    The following example shows you in a simplified manner how you can output either the error or the resource. Of course, it is also possible to call abort() manually if necessary.

    from flask import (
        Flask, 
        render_template, 
        request
    )
    from flask_restful import (
        Api, 
        Resource, 
        fields, 
        marshal_with, 
    )
    from flask_sqlalchemy import SQLAlchemy
    from sqlalchemy.orm import column_property
    import enum 
    
    
    class GenderChoices(enum.Enum):
        M = 'M'
        F = 'F'
    
        def __str__(self):
            return self.name
    
    app = Flask(__name__)
    app.config.from_mapping(
        SECRET_KEY='your secret here', 
        SQLALCHEMY_DATABASE_URI='sqlite:///demo.db', 
    )
    db = SQLAlchemy(app)
    api = Api(app)
    
    class Employee(db.Model):
        __tablename__ = "employees"
        id = db.Column(db.Integer, primary_key=True)
        
        firstname = db.Column(db.String(80), unique=False, nullable=False)
        lastname = db.Column(db.String(80), unique=False, nullable=False)
        name = column_property(firstname + ' ' + lastname)
        
        gender = db.Column(db.Enum(
            GenderChoices, 
            values_callable=lambda x: [str(member.value) for member in GenderChoices]
        ))
        
        # ...
    
        def to_dict(self):
            return {
                'employee_id': self.id,
                'employee_name': self.name,
                'gender': self.gender
                # ...
            }
    
    with app.app_context():
        db.drop_all()
        db.create_all()
    
        employee = Employee(
            firstname='User', 
            lastname='One', 
            gender=GenderChoices.M,
        )
        db.session.add(employee)
        db.session.commit()
    
    class EmployeeRsrc(Resource):
        @marshal_with({
            'data': fields.Nested({
                'employee_id': fields.Integer, 
                'employee_name': fields.String,  
                'gender': fields.String 
                # ...
            })
        })
        def get(self, employee_id):
            employee = db.get_or_404(
                Employee, employee_id, 
                description='Employee Not Found'
            )
            return {
                'data': employee.to_dict()
            }
    
    api.add_resource(EmployeeRsrc, '/employees/<employee_id>')
    

    If you want to output any error message including a status code, you can use abort(code, *args, **kwargs). Execution ends immediately and an exception is thrown, which is caught by the error handler, which sends a response.

    from flask_restful import abort
    
    class MyRsrc(Resource):
        def post(self):
            # ...
            abort(422, message='Email invalid')