Flask-RESTful APIで、ユーザーと都市の2つのオブジェクトがあるとします。これは1対多の関係です。 APIを作成してリソースを追加すると、非常に簡単で一般的なURLをマップするだけでできるようになります。コードは次のとおりです(役に立たないものは含まれていません)。
_class UserAPI(Resource): # The API class that handles a single user
def __init__(self):
# Initialize
def get(self, id):
# GET requests
def put(self, id):
# PUT requests
def delete(self, id):
# DELETE requests
class UserListAPI(Resource): # The API class that handles the whole group of Users
def __init__(self):
def get(self):
def post(self):
api.add_resource(UserAPI, '/api/user/<int:id>', endpoint='user')
api.add_resource(UserListAPI, '/api/users/', endpoint='users')
class CityAPI(Resource):
def __init__(self):
def get(self, id):
def put(self, id):
def delete(self, id):
class CityListAPI(Resource):
def __init__(self):
def get(self):
def post(self):
api.add_resource(CityListAPI, '/api/cities/', endpoint='cities')
api.add_resource(CityAPI, '/api/city/<int:id>', endpoint='city')
_
ご覧のとおり、非常に基本的な機能を実装するために必要なことはすべて実行できます。両方のオブジェクトを取得、投稿、配置、および削除できます。ただし、私の目標は2つあります。
(1)単なる都市IDではなく、都市名などの他のパラメーターで要求できるようにする。次のようになります。api.add_resource(CityAPI, '/api/city/<string:name>', endpoint='city')
ただし、このエラーはスローされません。
AssertionError:ビュー関数マッピングは既存のエンドポイント関数を上書きしています
(2)リクエストで2つのリソースを結合できるようにする。ある都市に関連付けられているすべてのユーザーを取得したいとします。 REST URLsでは、次のようになります。
_/api/cities/<int:id>/users
_
Flaskでこれを行うにはどうすればよいですか?どのエンドポイントにマップしますか?
基本的に、APIを基本から使用可能にする方法を探しています。アイデア/アドバイスをありがとう
あなたは2つの間違いを犯しています。
まず、Flask-RESTfulは、リソースが単一のURLで実装されていると考えるように導きます。実際には、同じタイプのリソースを返す多くの異なるURLを持つことができます。 Flask-RESTfulでは、URLごとに異なるResource
サブクラスを作成する必要がありますが、概念的にはこれらのURLは同じリソースに属します。実際には、リストと個々のリクエストを処理するために、リソースごとにすでに2つのインスタンスを作成していることに注意してください。
2つ目の間違いは、クライアントがAPIのすべてのURLを知っていることを期待していることです。これはAPIを構築するための良い方法ではありません。理想的には、クライアントはいくつかのトップレベルURLのみを知っていて、その後トップレベルURLからの応答のデータから残りを発見します。
APIで、/api/users
および/api/cities
をトップレベルAPIとして公開できます。個々の都市およびユーザーへのURLは応答に含まれます。たとえば、http://example.com/api/users
を呼び出してユーザーのリストを取得すると、次の応答が返される場合があります。
{
"users": [
{
"url": "http://example.com/api/user/1",
"name": "John Smith",
"city": "http://example.com/api/city/35"
},
{
"url": "http://example.com/api/user/2",
"name": "Susan Jones",
"city": "http://example.com/api/city/2"
}
]
}
ユーザーのJSON表現には、そのユーザーのURLと都市のURLが含まれていることに注意してください。クライアントはこれらのビルド方法を知っている必要はありません。
都市のURLは/api/city/<id>
であり、都市の完全なリストを取得するURLは、定義したとおり/api/cities
です。
名前で都市を検索する必要がある場合は、「都市」エンドポイントを拡張してそれを行うことができます。たとえば、/api/cities/<name>
という形式のURLを使用して、<name>
として指定された検索語に一致する都市のリストを返すことができます。
Flask-RESTfulでは、そのための新しいResource
サブクラスを定義する必要があります。例えば:
class CitiesByNameAPI(Resource):
def __init__(self):
# ...
def get(self, name):
# ...
api.add_resource(CitiesByNameAPI, '/api/cities/<name>', endpoint = 'cities_by_name')
クライアントが都市を要求すると、その都市のユーザーを取得するためのURLを含む応答を取得する必要があります。たとえば、上記の/api/users
応答から、最初のユーザーの都市について調べたいとしましょう。そのため、http://example/api/city/35
にリクエストを送信すると、次のJSONレスポンスが返されます。
{
"url": "http://example.com/api/city/35",
"name": "San Francisco",
"users": "http://example/com/api/city/35/users"
}
これで都市ができたので、その都市のすべてのユーザーを取得するために使用できるURLを取得しました。
クライアントはこれらのほとんどをゼロから構築する必要はなく、サーバーから取得するだけなので、URLがい、または構築が難しいことは重要ではありません。これにより、将来URLの形式を変更することもできます。
都市ごとにユーザーを取得するURLを実装するには、さらに別のResource
サブクラスを追加します。
class UsersByCityAPI(Resource):
def __init__(self):
# ...
def get(self, id):
# ...
api.add_resource(UsersByCityAPI, '/api/cities/<int:id>/users', endpoint = 'users_by_city')
これがお役に立てば幸いです!
リソースを複製せずにid/nameを実行できます。
api.add_resource(CitiesByNameAPI, '/api/cities/<name_or_id>', endpoint = 'cities_by_name')
class CitiesByNameAPI(Resource):
def get(self, name_or_id):
if name_or_id.isdigit():
city = CityModel.find_by_id(name_or_id)
else:
city = CityModel.find_by_name(name_or_id)
if city:
return city.to_json(), 200
return {'error': 'not found'}, 404
これによる悪影響があるかどうかはわかりません。