I’m trying to learn Python and create an app with OpenAPI, Swagger, Connexion and other tech (not relevant to the question).
I strated with a tutorial from RealPython which created 5 APIs (get all, get one, create, update and delete). 3 of the 5 work fine, but the create and update, which require the passing of an object, are failing. Below is the code and the exact error.
The swagger/OpenAPI file:
# swagger.yml
openapi: 3.0.0
info:
title: "Test"
description: "Testing"
version: "1.0.0"
servers:
- url: "/api"
components:
schemas:
Person:
type: object
properties:
name:
type: string
phone:
type: string
pattern: '^d{3}-d{3}-d{4}$' # 555-555-5555
email:
type: string
format: email
required:
- name
parameters:
name:
name: "name"
description: "Name of the person to get"
in: path
required: True
schema:
type: "string"
paths:
/people:
get:
operationId: "people.read_all"
tags:
- "People"
summary: "Read the list of people"
responses:
"200":
description: "Successfully read people list"
post:
operationId: "people.create"
tags:
- People
summary: "Create a person"
requestBody:
description: "Person to create"
required: True
content:
application/json:
schema:
x-body-name: "person"
$ref: "#/components/schemas/Person"
responses:
"201":
description: "Successfully created person"
/people/{name}:
get:
operationId: "people.read_one"
tags:
- People
summary: "Read one person"
parameters:
- $ref: "#/components/parameters/name"
responses:
"200":
description: "Successfully read person"
put:
tags:
- People
operationId: "people.update"
summary: "Update a person"
parameters:
- $ref: "#/components/parameters/name"
responses:
"200":
description: "Successfully updated person"
requestBody:
content:
application/json:
schema:
x-body-name: "person"
$ref: "#/components/schemas/Person"
delete:
tags:
- People
operationId: "people.delete"
summary: "Delete a person"
parameters:
- $ref: "#/components/parameters/name"
responses:
"204":
description: "Successfully deleted person"
Main application file:
# app.py
from flask import render_template
import connexion
app = connexion.App(__name__, specification_dir="./")
app.add_api("swagger.yml")
@app.route("/")
def home():
return render_template("home.html")
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000)
people (this is obviously just an example to be expanded later):
# people.py
from datetime import datetime
from flask import abort, make_response
def get_timestamp():
return datetime.now().strftime(("%Y-%m-%d %H:%M:%S"))
PEOPLE = {
"Tooth Fairy": {
"name": "Tooth Fairy",
"phone": "123-456-7890",
"timestamp": get_timestamp(),
},
"Knecht Ruprecht": {
"name": "Knecht Ruprecht",
"email": "[email protected]",
"timestamp": get_timestamp(),
},
"Easter Bunny": {
"name": "Easter Bunny",
"email": "[email protected]",
"timestamp": get_timestamp(),
}
}
def read_all():
return list(PEOPLE.values())
def create(person):
name = person.get("name")
phone = person.get("phone", "")
email = person.get("email", "")
if name and name not in PEOPLE:
PEOPLE[name] = {
"name": name,
"phone": phone,
"email": email,
"timestamp": get_timestamp(),
}
return PEOPLE[name], 201
else:
abort(
406,
f"Person with name {name} already exists",
)
def read_one(name):
if name in PEOPLE:
return PEOPLE[name]
else:
abort(
404, f"Person with last name {name} not found"
)
def update(name, person):
if name in PEOPLE:
PEOPLE[name]["name"] = person.get("name", PEOPLE[name])
PEOPLE[name]["phone"] = person.get("phone", PEOPLE[phone])
PEOPLE[name]["phone"] = person.get("phone", PEOPLE[phone])
PEOPLE[name]["timestamp"] = get_timestamp()
return PEOPLE[name]
else:
abort(
404,
f"Person with last name {name} not found"
)
def delete(name):
if name in PEOPLE:
del PEOPLE[name]
return make_response(
f"{name} successfully deleted", 200
)
else:
abort(
404,
f"Person with last name {name} not found"
)
My requirements with version numbers:
a2wsgi==1.10.4
anyio==4.3.0
asgiref==3.8.1
attrs==23.2.0
blinker==1.8.2
certifi==2024.2.2
charset-normalizer==3.3.2
click==8.1.7
colorama==0.4.6
connexion==3.0.6
Flask==3.0.3
flask-marshmallow==1.2.1
Flask-SQLAlchemy==3.1.1
greenlet==3.0.3
h11==0.14.0
httpcore==1.0.5
httptools==0.6.1
httpx==0.27.0
idna==3.7
inflection==0.5.1
iniconfig==2.0.0
itsdangerous==2.2.0
Jinja2==3.1.4
jsonschema==4.22.0
jsonschema-specifications==2023.12.1
MarkupSafe==2.1.5
marshmallow==3.21.2
packaging==24.0
pluggy==1.5.0
psycopg2-binary==2.9.9
pytest==8.2.0
python-dotenv==1.0.1
python-multipart==0.0.9
PyYAML==6.0.1
referencing==0.35.1
requests==2.31.0
rpds-py==0.18.1
sniffio==1.3.1
SQLAlchemy==2.0.30
starlette==0.37.2
swagger_ui_bundle==1.1.0
typing_extensions==4.11.0
urllib3==2.2.1
uvicorn==0.29.0
watchfiles==0.21.0
websockets==12.0
Werkzeug==3.0.3
And lastly, the error I’m getting:
TypeError: create() missing 1 required positional argument: 'person'
INFO: 127.0.0.1:36219 - "POST /api/people HTTP/1.1" 500 Internal Server Error
So, my question is, why isn’t create(person) receiving the person object?
The swagger UI loads and all the API’s are there:
Swagger
But when I try to simply use the example provided to create a person, it fails:
Create Request
Failure
Since I am new to these libraries, I am having trouble understanding what’s missing that’s causing the connection between the API definition and the create method to fail.
Thanks!
Tom
tommyc7 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.