app.py
하나로 api를 만들었음 <- 코드가 복잡해질 수록 "파일 분리"가 필요성은 커지게된다.app.py
, to_do.py
파일 2개로 api를 만들어보고자 함app.py
: API정보 및 기능이 구현된 파일을 가져와 기능별로 namespace를 부여해줌 => API 전체적인 그림을 그려주는 부분to_do.py
: 메소드 등 기능이 구현된 파일 => API 세부 동작(기능)을 구현하는 부분app.py
코드from flask import Flask
from flask_restx import Resource, Api
from to_do import Todo # 기능구현한 파일이 여기에서 import된다
app = Flask(__name__)
# api = Api(app) # 이전 실습에서는 여기까지만 구현했음
# 아래와 같이, version, title, description... 등의 내용들을 추가해서 넣어줄 수 있다.
api = Api(
app,
version='0.1',
title="Scene's API Server",
description="Scene's Todo API Server!",
terms_url="/",
contact="oneofakindscene@gmail.com",
license="MIT"
)
# 이 파일이 아닌 외부에 있는 파일(to_do.py)에 클래스를 구현하고 여기서 import한 다음 add_resource()를 통해 클래스를 등록
api.add_namespace(Todo, '/todos') # namespace에서 url 들어갈 부분 머로 들어가는지 확인해야함 => 여기선 /todos
if __name__ == "__main__":
app.run(debug=True, host='0.0.0.0', port=80)
api.add_namespace(Todo, '/todos')
<- api 객체를 특정 경로('/todos'
)에 등록 할 수 있게함http://localhost/todos
<- 이런식으로 api 호출할 때 사용http://localhost
이렇게만 접속해보면 아래 스샷과 같은 flask-RESTX
의 기본 기능으로 제공하는 Swagger 기반의 홈페이지에서 api의 정보들을 확인할 수 있다.version
: API Server의 버전을 명시합니다.title
: API Server의 이름을 명시합니다.description
: API Server의 설명을 명시합니다.terms_url
: API Server의 Base Url을 명시합니다.contact
: 제작자 E-Mail 등을 삽입합니다.license
: API Server의 라이센스를 명시 합니다.license_url
: API Server의 라이센스 링크를 명시 합니다app.py에는 아래와 같이 구현됨
api = Api(
app,
version='0.1',
title="Scene's API Server",
description="Scene's Todo API Server!",
terms_url="/",
contact="onoofakindscene@gmail.com",
license="MIT"
)
"""Comment"""
)를 이용하여 Document에 설명을 추가 할 수 있습니다.@Todo.route('')
class TodoPost(Resource):
@Todo.expect(todo_fields)
@Todo.response(201, 'Success', todo_fields_with_id)
def post(self):
"""post comment : Todo 리스트에 할 일을 등록 합니다."""
global count
global todos
idx = count
count += 1
todos[idx] = request.json.get('data')
return {
'todo_id': idx,
'data': todos[idx]
}, 201
Namespace 객체도 생성자 파라미터를 조정하여, 내용을 수정 할 수 있습니다.
title
: Namespace의 이름을 명시합니다.description
: Namespace의 설명을 명시합니다.todo.py 에 있는 Namespace 객체의 생성자 파라미터를 수정하면 아래와 같다.
Todo = Namespace(
name="Namespace의 name : Todos",
description="Namespace의 Description : Todo 리스트를 작성하기 위해 사용하는 API.",
)
name
, description
이 적용된 모습Namespace.model()
에 대해 설명 하자면, Namespace.model()
은 입력, 출력에 대한 스키마를 나타내는 객체flask_restx
내의 field 클래스
를 이용하여, 설명(description)
, 필수 여부(required)
, 예시(example)
를 넣을 수 있습니다.Namespace.inherit()
을 이용하여, Namespace.model()
을 상속 받을 수 있습니다.to_do.py 에 있는
Namespace.model()
,Namespace.inhert()
를 활용하여 입력, 출력에 대한 스키마 정의해주기
Todo = Namespace(
name="Namespace의 name : Todos",
description="Namespace의 Description : Todo 리스트를 작성하기 위해 사용하는 API.",
)
todo_fields = Todo.model('Todo', { # Model 객체 생성
'data': fields.String(description='a Todo', required=True, example="what to do")
})
todo_fields_with_id = Todo.inherit('Todo With ID', todo_fields, { # todo_fields 상속 받음
'todo_id': fields.Integer(description='a Todo ID')
})
to_do.py
파일에서 Namespace.model()
, Namespace.inherit()
을 사용해서 example 및 schema가 들어간 것을 확인할 수 있다.todo_fields
, todo_fields_with_id
부분임Namespace.doc()
데코레이터를 이용하여, Status Code 마다 설명을 추가하거나, 쿼리 파라미터에 대한 설명을 추가 할 수 있습니다.
params
: dict 객체를 받으며, 키
로는 파라미터 변수명
, 값
으로는 설명
을 적을 수 있습니다.responses
: dict 객체를 받으며, 키
로는 Status Code
(202, 500... 같은거), 값
으로는 설명
을 적을 수 있습니다.to_do.py 에서
Namespace.doc(params={dict})
,Namespace.doc(responses={dict})
적용된 부분
@Todo.route('/<int:todo_id>')
@Todo.doc(params={'todo_id': 'An ID'})
class TodoSimple(Resource):
[...]
@Todo.doc(responses={202: 'Success'})
@Todo.doc(responses={500: 'Failed'})
def delete(self, todo_id):
"""delete comment : To_do 리스트에 todo_id와 일치하는 ID를 가진 할 일을 삭제합니다."""
del todos[todo_id]
return {
"delete": "success"
}, 202
Namespace.Model
객체를 등록하면 됩니다.to_do.py 에서
Namespace.expect()
,Namespace.response()
된 부분
@Todo.route('')
class TodoPost(Resource):
@Todo.expect(todo_fields)
@Todo.response(201, 'Success', todo_fields_with_id)
def post(self):
"""post comment : To_do 리스트에 할 일을 등록 합니다."""
global count
global todos
idx = count
count += 1
todos[idx] = request.json.get('data')
return {
'todo_id': idx,
'data': todos[idx]
}, 201
Namespace.expect()
, Namespace.response()
적용된 스샷def
와 달리 return {dict}, 200
이런식으로 안들어가는 이유@Todo.route('/<int:todo_id>')
@Todo.doc(params={'todo_id': 'An ID'})
class TodoSimple(Resource):
@Todo.response(200, 'Success', todo_fields_with_id)
@Todo.response(500, 'Failed')
def get(self, todo_id):
"""Todo 리스트에 todo_id와 일치하는 ID를 가진 할 일을 가져옵니다."""
return {
'todo_id': todo_id,
'data': todos[todo_id]
}