Skip to content

Schemas should automatically respect API root location #4401

@nick-allen

Description

@nick-allen

Creating a schema should automatically provide any path prefixes to the registered router urls instead of defaulting to /.

It's possible to manually set this prefix using the DefaultRouter schema_url paramter, but this requires knowing where the router will be wired into Django URLs.

Steps to reproduce

Install django, rest framework, and coreapi. Configure normal Django boilerplate

Create a viewset

# viewsets.py

from rest_framework.viewsets import ModelViewSet
from rest_framework.serializers import ModelSerializer
from django.contrib.auth import get_user_model

User = get_user_model()


class UserSerializer(ModelSerializer):
    class Meta:
        model = User


class UserViewSet(ModelViewSet):

    queryset = User.objects.all()
    serializer_class = UserSerializer

Create a router with the schema_title argument to setup automatic CoreAPI renderer and register viewsets.
Include router URLs under a path not at root /

# urls.py
from django.conf.urls import url, include

from rest_framework.routers import DefaultRouter

from .viewsets import UserViewSet

v1_router = DefaultRouter(
    schema_title='Example API v1',
)

v1_router.register('users', UserViewSet)

urlpatterns = [
    url(r'^api/v1/', include(v1_router.urls, namespace='v1')),
]

Get schema document and verify url does not include API path prefix

$ curl http://localhost:8000/api/v1/?format=corejson
{
    "_type": "document",
    "_meta": {
        "title": "Example API v1"
    },
    "users": {
        "create": {
            "_type": "link",
            "url": "/users/",
            "action": "post",
            "encoding": "application/json",
            "fields": [
                {
                    "name": "password",
                    "required": true,
                    "location": "form"
                },
                {
                    "name": "last_login",
                    "location": "form"
                },
                {
                    "name": "is_superuser",
                    "location": "form",
                    "description": "Designates that this user has all permissions without explicitly assigning them."
                },
                {
                    "name": "username",
                    "required": true,
                    "location": "form",
                    "description": "Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only."
                },
                {
                    "name": "first_name",
                    "location": "form"
                },
                {
                    "name": "last_name",
                    "location": "form"
                },
                {
                    "name": "email",
                    "location": "form"
                },
                {
                    "name": "is_staff",
                    "location": "form",
                    "description": "Designates whether the user can log into this admin site."
                },
                {
                    "name": "is_active",
                    "location": "form",
                    "description": "Designates whether this user should be treated as active. Unselect this instead of deleting accounts."
                },
                {
                    "name": "date_joined",
                    "location": "form"
                },
                {
                    "name": "groups",
                    "location": "form",
                    "description": "The groups this user belongs to. A user will get all permissions granted to each of their groups."
                },
                {
                    "name": "user_permissions",
                    "location": "form",
                    "description": "Specific permissions for this user."
                }
            ]
        },
        "destroy": {
            "_type": "link",
            "url": "/users/{pk}/",
            "action": "delete",
            "fields": [
                {
                    "name": "pk",
                    "required": true,
                    "location": "path"
                }
            ]
        },
        "list": {
            "_type": "link",
            "url": "/users/",
            "action": "get",
            "fields": [
                {
                    "name": "page",
                    "location": "query"
                }
            ]
        },
        "partial_update": {
            "_type": "link",
            "url": "/users/{pk}/",
            "action": "patch",
            "encoding": "application/json",
            "fields": [
                {
                    "name": "pk",
                    "required": true,
                    "location": "path"
                },
                {
                    "name": "password",
                    "location": "form"
                },
                {
                    "name": "last_login",
                    "location": "form"
                },
                {
                    "name": "is_superuser",
                    "location": "form",
                    "description": "Designates that this user has all permissions without explicitly assigning them."
                },
                {
                    "name": "username",
                    "location": "form",
                    "description": "Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only."
                },
                {
                    "name": "first_name",
                    "location": "form"
                },
                {
                    "name": "last_name",
                    "location": "form"
                },
                {
                    "name": "email",
                    "location": "form"
                },
                {
                    "name": "is_staff",
                    "location": "form",
                    "description": "Designates whether the user can log into this admin site."
                },
                {
                    "name": "is_active",
                    "location": "form",
                    "description": "Designates whether this user should be treated as active. Unselect this instead of deleting accounts."
                },
                {
                    "name": "date_joined",
                    "location": "form"
                },
                {
                    "name": "groups",
                    "location": "form",
                    "description": "The groups this user belongs to. A user will get all permissions granted to each of their groups."
                },
                {
                    "name": "user_permissions",
                    "location": "form",
                    "description": "Specific permissions for this user."
                }
            ]
        },
        "retrieve": {
            "_type": "link",
            "url": "/users/{pk}/",
            "action": "get",
            "fields": [
                {
                    "name": "pk",
                    "required": true,
                    "location": "path"
                }
            ]
        },
        "update": {
            "_type": "link",
            "url": "/users/{pk}/",
            "action": "put",
            "encoding": "application/json",
            "fields": [
                {
                    "name": "pk",
                    "required": true,
                    "location": "path"
                },
                {
                    "name": "password",
                    "required": true,
                    "location": "form"
                },
                {
                    "name": "last_login",
                    "location": "form"
                },
                {
                    "name": "is_superuser",
                    "location": "form",
                    "description": "Designates that this user has all permissions without explicitly assigning them."
                },
                {
                    "name": "username",
                    "required": true,
                    "location": "form",
                    "description": "Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only."
                },
                {
                    "name": "first_name",
                    "location": "form"
                },
                {
                    "name": "last_name",
                    "location": "form"
                },
                {
                    "name": "email",
                    "location": "form"
                },
                {
                    "name": "is_staff",
                    "location": "form",
                    "description": "Designates whether the user can log into this admin site."
                },
                {
                    "name": "is_active",
                    "location": "form",
                    "description": "Designates whether this user should be treated as active. Unselect this instead of deleting accounts."
                },
                {
                    "name": "date_joined",
                    "location": "form"
                },
                {
                    "name": "groups",
                    "location": "form",
                    "description": "The groups this user belongs to. A user will get all permissions granted to each of their groups."
                },
                {
                    "name": "user_permissions",
                    "location": "form",
                    "description": "Specific permissions for this user."
                }
            ]
        }
    }
}

Expected behavior

Using coreapi cli, use any actions following schema tutorial in docs (http://www.django-rest-framework.org/tutorial/7-schemas-and-client-libraries/)

Get schema document

$ coreapi get http://localhost:8000/api/v1/
<Example API v1 "http://localhost:8000/api/v1/">
    users: {
        create(password, username, [last_login], [is_superuser], [first_name], [last_name], [email], [is_staff], [is_active], [date_joined], [groups], [user_permissions])
        destroy(pk)
        list([page])
        partial_update(pk, [password], [last_login], [is_superuser], [username], [first_name], [last_name], [email], [is_staff], [is_active], [date_joined], [groups], [user_permissions])
        retrieve(pk)
        update(pk, password, username, [last_login], [is_superuser], [first_name], [last_name], [email], [is_staff], [is_active], [date_joined], [groups], [user_permissions])
    }

Run list action

$ coreapi action users list
[
    {
        "id": 1,
        "password": "!YOnSaMeJECJU0oeULffq6tp6TB3WAlTLTngqXBpT",
        "last_login": null,
        "is_superuser": false,
        "username": "user",
        "first_name": "",
        "last_name": "",
        "email": "",
        "is_staff": false,
        "is_active": true,
        "date_joined": "2016-08-14T18:10:37.880469Z",
        "groups": [],
        "user_permissions": []
    }
]

Actual behavior

Get schema document

$ coreapi get http://localhost:8000/api/v1/

<Example API v1 "http://localhost:8000/api/v1/">
    users: {
        create(password, username, [last_login], [is_superuser], [first_name], [last_name], [email], [is_staff], [is_active], [date_joined], [groups], [user_permissions])
        destroy(pk)
        list([page])
        partial_update(pk, [password], [last_login], [is_superuser], [username], [first_name], [last_name], [email], [is_staff], [is_active], [date_joined], [groups], [user_permissions])
        retrieve(pk)
        update(pk, password, username, [last_login], [is_superuser], [first_name], [last_name], [email], [is_staff], [is_active], [date_joined], [groups], [user_permissions])
    }

Run list action

$ coreapi action users list
<Error: Not Found>
    message: "
              <!DOCTYPE html>
              <html lang=\"en\">
              <head>
                <meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">
                <title>Page not found at /users/</title>
                <meta name=\"robots\" content=\"NONE,NOARCHIVE\">
                <style type=\"text/css\">
                  html * { padding:0; margin:0; }
                  body * { padding:10px 20px; }
                  body * * { padding:0; }
                  body { font:small sans-serif; background:#eee; }
                  body>div { border-bottom:1px solid #ddd; }
                  h1 { font-weight:normal; margin-bottom:.4em; }
                  h1 span { font-size:60%; color:#666; font-weight:normal; }
                  table { border:none; border-collapse: collapse; width:100%; }
                  td, th { vertical-align:top; padding:2px 3px; }
                  th { width:12em; text-align:right; color:#666; padding-right:.5em; }
                  #info { background:#f6f6f6; }
                  #info ol { margin: 0.5em 4em; }
                  #info ol li { font-family: monospace; }
                  #summary { background: #ffc; }
                  #explanation { background:#eee; border-bottom: 0px none; }
                </style>
              </head>
              <body>
                <div id=\"summary\">
                  <h1>Page not found <span>(404)</span></h1>
                  <table class=\"meta\">
                    <tr>
                      <th>Request Method:</th>
                      <td>GET</td>
                    </tr>
                    <tr>
                      <th>Request URL:</th>
                      <td>http://localhost:8000/users/</td>
                    </tr>

                  </table>
                </div>
                <div id=\"info\">

                    <p>
                    Using the URLconf defined in <code>drfbug.urls</code>,
                    Django tried these URL patterns, in this order:
                    </p>
                    <ol>

                        <li>

                              ^api/v1/


                        </li>

                    </ol>
                    <p>The current URL, <code>users/</code>, didn't match any of these.</p>

                </div>

                <div id=\"explanation\">
                  <p>
                    You're seeing this error because you have <code>DEBUG = True</code> in
                    your Django settings file. Change that to <code>False</code>, and Django
                    will display a standard 404 page.
                  </p>
                </div>
              </body>
              </html>
              "

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      pFad - Phonifier reborn

      Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

      Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy