95200
urls.pyのurlpatternsの記述方法を教えてください。

例えば、以下のように、URLにつなぎ足すことで、処理を振り分けたいと考えています。  
しかし、この記述方法であると、book/のみ認識され、すべてBookALLAPIViewに振り分けられてしまいます。  
<book_key>にあたるURLを記述した場合、postmanで、最後に/が入らないとエラーだと判断されてしまいます。  
一番下のpath('book/',...)のみ宣言した場合だど、最後に/の有無にかかわらず処理ができます。

```

    urlpatterns = [
        path('version/',VersionAPIView.as_view()),
        path('book/<book_key>/price/<command>/',BookPriceAPIView.as_view()),
        path('book/<book_key>/price/',BookPriceAPIView.as_view()),
        path('book/<book_key>/＜command>/',BookAPIView.as_view()),
        path('book/<book_key>/',BookAPIView.as_view()),
        path('book/',BookALLAPIView.as_view()),
    ]
    
```

# DRFパッケージ

追加調査により、以下のパッケージが有効なようです。  
[drf-nested-routers](https://github.com/alanjds/drf-nested-routers)

## このパッケージの要件

Django: (2.2, 3.0, 3.1, 3.2, 4.0)  
Python: (3.7, 3.8, 3.9, 3.10, 3.11)  
Django REST framework: (3.11, 3.12, 3.13)

この要件に伴い、以下の環境にしました。  
Django:4.0  
Python:3.11  
Django REST framework:3.13

本パッケージの正常な使用方法が分かればご教示ください。
CC BY-SA 4.0
2023-06-13T00:42:09.677
95195
2023-06-13T20:57:12.293
2023-06-13T04:23:53.817
32891
32891
question
python django-rest-framework
Django REST framework APIの振り分け方法およびrest_framework_nestedの使用方法
自己解決できたようなので、投稿します。

drf-nested-routersパッケージを使用します。  
この例では、modelsで定義したデータベースとリンクしない形でAPI応答する処理となっています。

# このパッケージの要件

Django: (2.2, 3.0, 3.1, 3.2, 4.0)  
Python: (3.7, 3.8, 3.9, 3.10, 3.11)  
Django REST framework: (3.11, 3.12, 3.13)

# フォルダ構成

api:rest framework用アプリケーション  
app:WEBサーバー用アプリケーション  
config:Djangoプロジェクト

フォルダツリー

```

    .
    ├── api
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── apps.py
    │   ├── migrations
    │   │   ├── __init__.py
    │   ├── serializers.py
    │   ├── templates
    │   │   └── api
    │   │       └── version.html
    │   ├── tests.py
    │   ├── urls.py
    │   └── views.py
    ├── app
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── apps.py
    │   ├── migrations
    │   │   ├── 0001_initial.py
    │   │   ├── __init__.py
    │   ├── models.py
    │   ├── templates
    │   │   └── app
    │   │       └── index.html
    │   ├── test
    │   │   ├── test_models.py
    │   │   └── test_models_handle.py
    │   ├── tests.py
    │   ├── urls.py
    │   └── views.py
    ├── config
    │   ├── __init__.py
    │   ├── asgi.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    ├── db.sqlite3
    └── manage.py
    
```

# ソースコード

config/urls.pyにおいて、urlパスを各アプリケーションに振り分ける。

```

    urlpatterns = [
        path('app/', include('app.urls')),
        path('api/', include('api.urls')),
    ]
    
```

api/urls.py  
**registerで登録する順序が重要です。**

```

    from django.urls import path, include
    
    from rest_framework_nested import routers
    from .views import *
    
    router = routers.SimpleRouter()
    router.register('book/open', BookOpenViewSet, basename='book_open')
    router.register('book/close', BookCloseViewSet, basename='book_close')
    router.register('book', BookViewSet, basename='book')
    
    book_router = routers.NestedSimpleRouter(router, r'book', lookup='book')
    book_router.register(r'open', BookKeyOpenViewSet, basename='book_open')
    
    urlpatterns = [
        path('v1/', include(router.urls)),
        path('v1/', include(book_router.urls)),
        path('version/', VersionView),
    ]
    
```

api/views.py

```

    from django.shortcuts import render
    
    import json
    
    from rest_framework import viewsets
    from django.http import JsonResponse
    
    def VersionView(request, *args,**kwargs):
        params = {
            'version':
                json.dumps({'version':'ver 1.2'},indent=2)
        }
        return render(request,"api/version.html",params)
    
    
    class BookViewSet(viewsets.ViewSet):
        def list(self, requset):
            return JsonResponse({"book":"all book list"}, json_dumps_params={'indent': 2})
    
        def retrieve(self, request, pk=None):
            return JsonResponse({"book":f"book id = {pk}"}, json_dumps_params={'indent': 2})
    
        def create(self, request):
            # POST
            return JsonResponse({"book":f"book create"}, json_dumps_params={'indent': 2})
    
    class BookOpenViewSet(viewsets.ViewSet):
        def list(self, requset):
            return JsonResponse({"book":"book open command"}, json_dumps_params={'indent': 2})
    
    class BookCloseViewSet(viewsets.ViewSet):
        def list(self, requset):
            return JsonResponse({"book":"book cloase command"}, json_dumps_params={'indent': 2})
    
    class BookKeyOpenViewSet(BookOpenViewSet):
        def list(self, request, book_pk=None):
            return JsonResponse({"book":f"book({book_pk}) open command"}, json_dumps_params={'indent': 2})
        
        def retrieve(self, request, pk=None, book_pk=None):
            return JsonResponse({"book":f"book({book_pk}) open command({pk})"}, json_dumps_params={'indent': 2})
    
```

# URLパス

url | response  
---|---  
[GET]<http://127.0.0.1:8000/api/version> | { "version": "ver 1.2" }  
[GET]<http://127.0.0.1:8000/api/v1/book> | { "book": "all book list" }  
[POST]<http://127.0.0.1:8000/api/v1/book/> | { "book": "book create" }  
[GET]<http://127.0.0.1:8000/api/v1/book/1> | { "book": "book id = 1" }  
[GET]<http://127.0.0.1:8000/api/v1/book/open> | { "book": "book open command"
}  
[GET]<http://127.0.0.1:8000/api/v1/book/close> | { "book": "book close
command" }  
[GET]<http://127.0.0.1:8000/api/v1/book/1/open> | { "book": "book(1) open
command" }  
[GET]<http://127.0.0.1:8000/api/v1/book/1/open/2> | { "book": "book(1) open
command(2)" }  
  
**注意：**  
POSTコマンドの末尾`/`が必須です。  
この理由がお分かりの方はコメントにて追加情報をいただけるとありがたいです。
CC BY-SA 4.0
2023-06-13T06:09:55.903
95200
2023-06-13T15:35:58.617
2023-06-13T15:35:58.617
3060
32891
95195
answer