Flask HTTP methods | Learning Flask Ep. 19
Understanding when and where to use GET, POST, PUT, PATCH, DELETE and a brief introduction to HTTP methods in Flask
Flask supports the common HTTP methods, including GET
, POST
, PUT
, PATCH
, DELETE
and working with them is extremely simple, allowing us to build URL’s and endpoints which only listen for certain HTTP methods.
In this part of the “Learning Flask” series, we’re going to build a simple application to demonstrate working with the 5 HTTP methods listed above, along with examples of when and how to use them.
If you’re new to programming or new to working on web, getting to know the HTTP methods and understanding the basics of HTTP is extremely valuable, so I’d suggest reading up on it after you’re done here.
This guide isn’t designed to be an in depth tutorial on HTTP, however it should give you enough information to use them in your Flask applications and hopefully you’ll learn something new!
HTTP basics
The “Hyper text transfer protocol” AKA HTTP is the world wide protocol for how we communicate and send/receive information on the web.
It works on a “Request/Response” protocol, where a client makes a request to a web server which then returns a response.
For example, when you clicked on this very URL, your browser (The client) made a request to the Pythonise web server, which internally triggered a series of events to return a response, which is the HTML you’re reading now.
Like I said, this is a very basic introduction to HTTP and we’re not going to go into too much detail on it in this guide. For more information, the Wikipedia page is a good place to start.
HTTP status codes
When something goes well, it’s nice to get a pat on the back, likewise when something doesn’t go as planned, it helps to know why so you can put it right.
Feedback is very important and it’s no different on the web!
HTTP status codes are used as a feedback system, issued by the server in response to a request from the client to indicate the status of the request.
If the request went well, the server returns a status code to the client to indicate that everything is OK, likewise a bad request or error will result in a different code to tell the client that something didn’t go as expected.
If you’ve spend any time on the web, you’ve most likely come across these status codes yourself.
Ever clicked on a link and got a 404 NOT FOUND
? That’s the web servers way of saying “I got your message, but whatever you’re looking for, I don’t have it”. However rather than saying that it simply returns a 404
status code (Which is much more friendly for machines to understand 🤖)
HTTP status codes are made up of 3 digits that fall into 5 categories, with each category representing a certain class of code.
The first digit is the category and the 5 categories correspond to the following class:
1xx
- Informational2xx
- Success3xx
- Redirection4xx
- Client errors5xx
- Server errors
The example of the 404
status code falls under the “client error” category, where the client tried to request something that doesn’t exist on the server.
The last 2 digits of the code don’t fall under any kind of class or category, but are used to provide more information and context.
Again to use the 404
example, the last 2 digits refer to NOT FOUND
, giving more context to the type of client error.
A full list of HTTP status codes can be found here at the Wiki.
HTTP methods
The method is the type of action you want the request to perform and is sent from the client to the server on every request.
There are several HTTP methods but we’re only going to cover 5 in this article:
GET
- Used to fetch the specified resourcePOST
- Used to create new data at the specified resourcePUT
- Used to create new data or replace existing data at the specified resourcePATCH
- Used to create new data or update/modify existing data at the specified resourceDELETE
- Used to delele existing data at the specified resource
Requesting a URL is an example of a GET
request, where your browser makes a request for resources at a specified location (the URL) and the server returns some HTML. GET
requests are “safe” as they aren’t able to modify state or data on the server.
An example use case of a POST
request would be creating a new account on a website or application, whereby the resource doesn’t already exist.
Flask HTTP methods
By default, routes created with @app.route(/example)
only listen and respond to GET
requests and have to be instructed to listen and respond to other methods using the methods
keyword & passing it a list of request methods.
Let’s explore some common use cases for GET
requests.
GET requests
Ubiquitously used in Flask applications, the GET
method is used to return data at a specified resource/location.
Returning text
Possible one of the most simple routes you can write in a flask app simply returns a string:
@app.route("/get-text")
def get_text():
return "some text"`
Rendering templates
A very common use case for a GET
request is to return some HTML:
from flask import render template
@app.route("/")
def index():
return render_template("index.html")
Handlinq query strings
No different from either of the 2 previous examples, with the addition of handling a query string in the URL and returning a formatted string to the client:
from flask import request
@app.route("/qs")
def qs():
if request.args:
req = request.args
return " ".join(f"{k}: {v} " for k, v in req.items())
return "No query"
Requesting /qs?name=john&language=python
returns the string name: john language: python
.
Fetching resources
Again, not really any different from the previous examples, just in this case fetching and returning a resource as a JSON string. We’ve also created a mock database called stock
:
from flask import make_response, jsonify
stock = {
"fruit": {
"apple": 30,
"banana": 45,
"cherry": 1000
}
}
@app.route("/stock")
def get_stock():
res = make_response(jsonify(stock), 200)
return res
Extending the previous example with some URL variables, ding a lookup and returning a JSON response (Note the use of the 404
if the collection or member is not found):
@app.route("/stock/<collection>")
def get_collection(collection):
""" Returns a collection from stock """
if collection in stock:
res = make_response(jsonify(stock[collection]), 200)
return res
res = res = make_response(jsonify({"error": "Not found"}), 404)
return res
@app.route("/stock/<collection>/<member>")
def get_member(collection, member):
""" Returns the qty of the collection member """
if collection in stock:
member = stock[collection].get(member)
if member:
res = make_response(jsonify(member), 200)
return res
res = make_response(jsonify({"error": "Not found"}), 404)
return res
res = res = make_response(jsonify({"error": "Not found"}), 404)
return res
In summary, use GET
requests when you just need to return resources to the client and NOT make any changes to the state/data of your application.
GET and POST
In many cases such as rendering forms, you’ll need a route to handle more than just GET
requests.
Making any request other than GET
to a route without the methods
argument and a list of methods will result in a 405 METHOD NOT ALLOWED
HTTP status code, as methods must be declared for the route to respond to.
Adding multiple request methods to a route is done with the following:
@app.route("/example", methods=["METHOD_A", "METHOD_B"])
# GET POST PUT PATCH DELETE etc..
The route will now listen and respond to both methods provided which means we have to put in some control flow to handle each type of request.
Fortunately this is made easy using the request
object, in particular the request.method
attribute which returns the method for the current request.
This verbose and silly example illustrates the logic, assuming we want to listen for GET
and POST
requests:
@app.route("/log-in", methods=["GET", "POST"])
def log_in():
if request.method == "POST":
# Attempt the login & do something else
elif request.method == "GET":
return render_template("log_in.html")
As Flask routes default to GET
requests, we can remove the elif
statement and let Flask return the template:
@app.route("/log-in", methods=["GET", "POST"])
def log_in():
if request.method == "POST":
# Only if the request method is POST
# attempt the login & do something else
# Otherwise default to this
return render_template("log_in.html")`
A working example can be seen below where we’re rendering a template which a user can then use to add a new collection to our stock
database:
@app.route("/add-collection", methods=["GET", "POST"])
def add_collection():
"""
Renders a template if request method is GET.
Creates a collection if request method is POST
and if collection doesn't exist
"""
if request.method == "POST":
req = request.form
collection = req.get("collection")
member = req.get("member")
qty = req.get("qty")
if collection in stock:
message = "Collection already exists"
return render_template("add_collection.html", stock=stock, message=message)
stock[collection] = {member: qty}
message = "Collection created"
return render_template("add_collection.html", stock=stock, message=message)
return render_template("add_collection.html", stock=stock)
POST requests
Flask routes listen for GET
requests by default, so we must implicitly instruct them to listen for anything other than GET
.
In the example below, we’ve passed methods=["POST"]
to the @app.route()
decorator, meaning this route will ONLY respond to POST
requests:
@app.route("/stock/<collection>", methods=["POST"])
def create_collection(collection):
""" Creates a new collection if it doesn't exist """
req = request.get_json()
if collection in stock:
res = make_response(jsonify({"error": "Collection already exists"}), 400)
return res
stock.update({collection: req})
res = make_response(jsonify({"message": "Collection created"}), 201)
return res
We’re checking to see if the collection
variable (passed in via the URL) is in our stock
database and if not, create it, otherwise return a 400 BAD REQUEST
to indicate the resource already exists.
POST
requests should be used to create NEW resources (New users, devices, posts, articles, datasets etc..)
Again, if you tried to access this resource from the browser, you’d be greeted with a 405 METHOD NOT ALLOWED
status as we’ve not provided GET
as one of the available request methods to listen for.
PUT requests
PUT
requests are similar to POST
requests but serve a very different purpose.
As we explained earlier, PUT
should be used to create or replace a resource, meaning if it doesn’t exist - create it, however if it does exist, replace it.
Check out the example below:
@app.route("/stock/<collection>", methods=["PUT"])
def put_collection(collection):
""" Replaces or creates a collection """
req = request.get_json()
if collection in stock:
stock[collection] = req
res = make_response(jsonify({"message": "Collection replaced"}), 200)
return res
stock[collection] = req
res = make_response(jsonify({"message": "Collection created"}), 201)
return res
Since PUT
requests shouldn’t care about the existing data or resource, we’ve decided to go ahead and create the resource with no regard for any existing data. The only dirrerence in the logic is the HTTP status code.
If the collection DIDN’T exist, we’re returning a 201 CREATED
status. Whereas if the collection DID exist, we’re returning a 200 OK
to indicate that the collection has been replaced.
It’s probably bad design to replace an entire collection using the
PUT
request, but for demonstrational purposes it’ll do. A better solution would be to usePUT
to replace or create a value for one of the members in the collection.
PATCH requests
We’re using a PATCH
request to update OR create a resource in our stock
database:
@app.route("/stock/<collection>", methods=["PATCH"])
def patch_collection(collection):
""" Updates or creates a collection """
req = request.get_json()
if collection in stock:
for k, v in req.items():
stock[collection][k] = v
res = make_response(jsonify({"message": "Collection updated"}), 200)
return res
stock[collection] = req
res = make_response(jsonify({"message": "Collection created"}), 201)
return res
Rather than replace the collection entirely, we’re iterating over the keys and values in the request body and updating the values in the collection, only creating new members if they don’t exist.
And just like in PUT
, we’re returning a 200
if the collection was updated and a 201
if the collection was created.
Let’s cover the last request method in this article, DELETE
.
Delete requests
Just like it says on the tin, DELETE
requests should be used to delete a resource.
As per the rest of the examples, we provide methods=["DELETE"]
in the @app.route()
decorator, meaning this route will only listen for that specific method.
In the 2 examples below, you’ll see we’re deleting a collection and deleting individual members from a collection with 2 separate routes:
@app.route("/stock/<collection>", methods=["DELETE"])
def delete_collection(collection):
""" If the collection exists, delete it """
if collection in stock:
del stock[collection]
res = make_response(jsonify({}), 204)
return res
res = make_response(jsonify({"error": "Collection not found"}), 404)
return res
@app.route("/stock/<collection>/<member>", methods=["DELETE"])
def delete_member(collection, member):
""" If the collection exists and the member exists, delete it """
if collection in stock:
if member in stock[collection]:
del stock[collection][member]
res = make_response(jsonify({}), 204)
return res
res = make_response(jsonify({"error": "Member not found"}), 404)
return res
res = make_response(jsonify({"error": "Collection not found"}), 404)
return res
On deletion, we’re returning a 204 NO CONTENT
status code to indicate a successful transaction and we have no content to return.
If the resource isn’t found, I/e the collection or member doesn’t exist, we’re returning a 404 NOT FOUND
.
Wrapping up
The code snippets shown here were designed to demonstrate how to work with some of the common request methods in Flask, rather than be examples of how to write a REST API, so take the examples with a pinch of salt.
restfulapi.net is a great place to learn more about API design and using the various request methods, along with the 2 Wikipedia links below.
I hope you learned something new!
- https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
- https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol
Last modified · 28 Feb 2019
Source : .