Update and Delete with Flask-RESTful (CodeGrade)

Learning Goals


Key Vocab


Introduction

Now that we've explored how to create and retrieve records with POST and GET, let's look into updating records individually and in batches with PATCH.

Run pipenv install && pipenv shell to enter your virtual environment.

$ pipenv install && pipenv shell

Change into the server directory to create the database and populate it with seed data:

$ cd server
$ flask db upgrade
$ python seed.py

Adding a PATCH Route

We're just about pros with Flask-RESTful now: if you're feeling confident, go ahead and try adding a patch() method to NewsletterByID.

**TIP: Remember to use setattr() to cut down the number of lines of code!

Solution below...

...

...

...

Just as with the get() route in this resource, we're going to pass in the id from the URL and use it to retrieve the record we're updating. Unlike the get() route, we want to leave it as a Newsletter object instead of converting it to a dictionary, since we want to change the attributes on the record.

Looping through the form data gives us its keys, the attribute names to be changed. From there, we can set each attribute on the Newsletter object to its new value with setattr(). From here, we update the database with the new record, create a response with the record, and send it back to the client.

class NewsletterByID(Resource):

    def get(self, id):

        response_dict = Newsletter.query.filter_by(id=id).first().to_dict()

        response = make_response(
            response_dict,
            200,
        )

        return response

    def patch(self, id):

        record = Newsletter.query.filter_by(id=id).first()
        for attr in request.form:
            setattr(record, attr, request.form[attr])

        db.session.add(record)
        db.session.commit()

        response_dict = record.to_dict()

        response = make_response(
            response_dict,
            200
        )

        return response

Try it out for yourself: open Postman and navigate to http://127.0.0.1:5000/newsletters/20 Links to an external site.. Change the request method to PATCH, edit the Body > form-data with a new body, then hit submit. You should see a response similar to the following:

{
  "body": "blah blah blah blah blah blah blah",
  "edited_at": "2022-09-22 16:50:06",
  "id": 20,
  "published_at": "2022-09-22 16:48:12",
  "title": "Mr. Title"
}

Looks like someone didn't enjoy the newsletter from September 22!


Adding a DELETE Route

Now that we've identified an unpopular newsletter, it might be a good idea to head into the API and delete it. To accomplish that, we'll have to add a delete() route to NewsletterByID:

class NewsletterByID(Resource):

    def delete(self, id):

        record = Newsletter.query.filter_by(id=id).first()

        db.session.delete(record)
        db.session.commit()

        response_dict = {"message": "record successfully deleted"}

        response = make_response(
            response_dict,
            200
        )

        return response

Here, we retrieve the record using the id passed to the route through the URL, delete it, and return a message that the record has been successfully deleted:

{
  "message": "record successfully deleted"
}

Conclusion

While there are a few nuances that separate Flask-RESTful from vanilla Flask in building APIs, we can see that they exist to simplify the process of making a RESTful API: rather than specifying the accepted HTTP request methods and handling them with if/elif/else blocks, methods are used to neatly organize the routes for GET, POST, PATCH, DELETE, and more at each URL.


Solution Code

# server/app.py

#!/usr/bin/env python3

from flask import Flask, request, make_response
from flask_migrate import Migrate
from flask_restful import Api, Resource

from models import db, Newsletter

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///newsletters.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.json.compact = False

migrate = Migrate(app, db)
db.init_app(app)

api = Api(app)

class Index(Resource):

    def get(self):

        response_dict = {
            "index": "Welcome to the Newsletter RESTful API",
        }

        response = make_response(
            response_dict,
            200,
        )

        return response

api.add_resource(Index, '/')

class Newsletters(Resource):

    def get(self):

        response_dict_list = [n.to_dict() for n in Newsletter.query.all()]

        response = make_response(
            response_dict_list,
            200,
        )

        return response

    def post(self):

        new_record = Newsletter(
            title=request.form['title'],
            body=request.form['body'],
        )

        db.session.add(new_record)
        db.session.commit()

        response_dict = new_record.to_dict()

        response = make_response(
            response_dict,
            201,
        )

        return response

api.add_resource(Newsletters, '/newsletters')

class NewsletterByID(Resource):

    def get(self, id):

        response_dict = Newsletter.query.filter_by(id=id).first().to_dict()

        response = make_response(
            response_dict,
            200,
        )

        return response

    def patch(self, id):

        record = Newsletter.query.filter_by(id=id).first()
        for attr in request.form:
            setattr(record, attr, request.form[attr])

        db.session.add(record)
        db.session.commit()

        response_dict = record.to_dict()

        response = make_response(
            response_dict,
            200
        )

        return response

    def delete(self, id):

        record = Newsletter.query.filter_by(id=id).first()

        db.session.delete(record)
        db.session.commit()

        response_dict = {"message": "record successfully deleted"}

        response = make_response(
            response_dict,
            200
        )

        return response

api.add_resource(NewsletterByID, '/newsletters/<int:id>')

if __name__ == '__main__':
    app.run(port=5555, debug=True)

Resources

Powered by Forestry.md