Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inquiry Regarding Organization of CRUD Operations in Flask-RESTx #598

Open
Macktireh opened this issue Mar 31, 2024 · 1 comment
Open

Inquiry Regarding Organization of CRUD Operations in Flask-RESTx #598

Macktireh opened this issue Mar 31, 2024 · 1 comment
Labels
question Further information is requested

Comments

@Macktireh
Copy link

Ask a question

Hello,

I've tested the example of the Todo API that you provided. It's structured using classes to define the API resources. However, I'm wondering if it's possible to write the code in a different way by grouping all CRUD operations into a single controller class called TodoController.

Here's the initial example I retrieved from Flask-RESTx documentation:

from flask import Flask
from flask_restx import Api, Resource, fields
from werkzeug.middleware.proxy_fix import ProxyFix

app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app)
api = Api(
    app,
    version="1.0",
    title="TodoMVC API",
    description="A simple TodoMVC API",
)

ns = api.namespace("todos", description="TODO operations")

todo = api.model(
    "Todo",
    {
        "id": fields.Integer(readonly=True, description="The task unique identifier"),
        "task": fields.String(required=True, description="The task details"),
    },
)


class TodoDAO:
    def __init__(self):
        self.counter = 0
        self.todos = []

    def get(self, id):
        for todo in self.todos:
            if todo["id"] == id:
                return todo
        api.abort(404, f"Todo {id} doesn't exist")

    def create(self, data):
        todo = data
        todo["id"] = self.counter = self.counter + 1
        self.todos.append(todo)
        return todo

    def update(self, id, data):
        todo = self.get(id)
        todo.update(data)
        return todo

    def delete(self, id):
        todo = self.get(id)
        self.todos.remove(todo)


DAO = TodoDAO()
DAO.create({"task": "Build an API"})
DAO.create({"task": "?????"})
DAO.create({"task": "profit!"})


@ns.route("/")
class TodoList(Resource):
    """Shows a list of all todos, and lets you POST to add new tasks"""

    @ns.doc("list_todos")
    @ns.marshal_list_with(todo)
    def get(self):
        """List all tasks"""
        return DAO.todos

    @ns.doc("create_todo")
    @ns.expect(todo)
    @ns.marshal_with(todo, code=201)
    def post(self):
        """Create a new task"""
        return DAO.create(api.payload), 201


@ns.route("/<int:id>")
@ns.response(404, "Todo not found")
@ns.param("id", "The task identifier")
class Todo(Resource):
    """Show a single todo item and lets you delete them"""

    @ns.doc("get_todo")
    @ns.marshal_with(todo)
    def get(self, id):
        """Fetch a given resource"""
        return DAO.get(id)

    @ns.doc("delete_todo")
    @ns.response(204, "Todo deleted")
    def delete(self, id):
        """Delete a task given its identifier"""
        DAO.delete(id)
        return "", 204

    @ns.expect(todo)
    @ns.marshal_with(todo)
    def put(self, id):
        """Update a task given its identifier"""
        return DAO.update(id, api.payload)


if __name__ == "__main__":
    app.run(debug=True)

And here's the reorganized version with the TodoController class:

from flask import Flask
from flask_restx import Api, Resource, fields
from werkzeug.middleware.proxy_fix import ProxyFix

app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app)
api = Api(
    app,
    version="1.0",
    title="TodoMVC API",
    description="A simple TodoMVC API",
)

ns = api.namespace("todos", description="TODO operations")

todo = api.model(
    "Todo",
    {
        "id": fields.Integer(readonly=True, description="The task unique identifier"),
        "task": fields.String(required=True, description="The task details"),
    },
)


class TodoDAO:
    def __init__(self):
        self.counter = 0
        self.todos = []

    def get(self, id):
        for todo in self.todos:
            if todo["id"] == id:
                return todo
        api.abort(404, f"Todo {id} doesn't exist")

    def create(self, data):
        todo = data
        todo["id"] = self.counter = self.counter + 1
        self.todos.append(todo)
        return todo

    def update(self, id, data):
        todo = self.get(id)
        todo.update(data)
        return todo

    def delete(self, id):
        todo = self.get(id)
        self.todos.remove(todo)


DAO = TodoDAO()
DAO.create({"task": "Build an API"})
DAO.create({"task": "?????"})
DAO.create({"task": "profit!"})


@ns.route("/")
class TodoController(Resource):
    
    @ns.doc("list_todos")
    @ns.marshal_list_with(todo)
    @ns.get('')
    def getTodos(self):
        """List all tasks"""
        return DAO.todos

    @ns.doc("create_todo")
    @ns.expect(todo)
    @ns.marshal_with(todo, code=201)
    @ns.post('')
    def createTodo(self):
        """Create a new task"""
        return DAO.create(api.payload), 201

    @ns.doc("get_todo")
    @ns.marshal_with(todo)
    @ns.get('/<int:id>')
    def getTodo(self, id):
        """Fetch a given resource"""
        return DAO.get(id)
    
    @ns.doc("delete_todo")
    @ns.response(204, "Todo deleted")
    @ns.delete('/<int:id>')
    def deleteTodo(self, id):
        """Delete a task given its identifier"""
        DAO.delete(id)
        return "", 204
    
    @ns.doc("update_todo")
    @ns.expect(todo)
    @ns.marshal_with(todo)
    @ns.put('/<int:id>')
    def updateTodo(self, id):
        """Update a task given its identifier"""
        return DAO.update(id, api.payload)


if __name__ == "__main__":
    app.run(debug=True)

Is it possible to adopt this structure to manage CRUD operations in a more concise and organized manner? Additionally, will Flask-RESTx support this type of structure in the future?

Thank you.

@Macktireh Macktireh added the question Further information is requested label Mar 31, 2024
@peter-doggart
Copy link
Contributor

I don't think this is currently possible. If you have a look at the code for the flask-restx resource class, it matches method names against the incoming HTTP request.method.

You could probably write your own custom Resource to structure the code in this way, but it's unlikely flask-restx will make this possible going forwards.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants