Initial Refactor Step

* Add descriptor class
* call from generator
* proxy back to generator for implementation.
This commit is contained in:
Carlton Gibson 2017-08-23 10:11:04 +02:00
parent 9aaea2586b
commit 8f13e9c143
2 changed files with 62 additions and 2 deletions

View File

@ -256,6 +256,38 @@ class EndpointInspector(object):
]
class APIViewSchemaDescriptor(object):
"""
Descriptor class on APIView.
Responsible for per-view instrospection and schema generation.
"""
def __get__(self, instance, owner):
# ???: Is this TOO simple? (Option is to return a new instance each time.)
self.view = instance
return self
def get_link(self, path, method, generator):
"""
Generate `coreapi.Link` for view.
This is the main _public_ access point.
"""
assert self.view is not None, "Schema generation REQUIRES a view instance. (Hint: you accessed `schema` from the view CLASS rather than an instance.)"
view = self.view
# TEMP: now we proxy back to the generator
link = generator.get_link(path, method, view)
return link
# TODO: Where should this live?
# - We import APIView here. So we can't import the descriptor into `views`
# - APIView is only used by SchemaView.
# - ???: Make `schemas` a package and move SchemaView to `schema.views`
APIView.schema = APIViewSchemaDescriptor()
class SchemaGenerator(object):
# Map HTTP methods onto actions.
default_mapping = {
@ -341,7 +373,7 @@ class SchemaGenerator(object):
for path, method, view in view_endpoints:
if not self.has_view_permissions(path, method, view):
continue
link = self.get_link(path, method, view)
link = self.get_link_proxy(path, method, view)
subpath = path[len(prefix):]
keys = self.get_keys(subpath, method, view)
insert_into(links, keys, link)
@ -434,6 +466,18 @@ class SchemaGenerator(object):
return path.replace('{pk}', '{%s}' % field_name)
# Methods for generating each individual `Link` instance...
def get_link_proxy(self, path, method, view):
"""
Proxy to view's descriptor for link
TEMPORARY NAME at least.
"""
schema = view.schema
# To begin we pass generator — we still need its methods.
link = schema.get_link(path, method, self)
return link
def get_link(self, path, method, view):
"""

View File

@ -1,5 +1,6 @@
import unittest
import pytest
from django.conf.urls import include, url
from django.core.exceptions import PermissionDenied
from django.http import Http404
@ -10,7 +11,9 @@ from rest_framework.compat import coreapi, coreschema
from rest_framework.decorators import detail_route, list_route
from rest_framework.request import Request
from rest_framework.routers import DefaultRouter
from rest_framework.schemas import SchemaGenerator, get_schema_view
from rest_framework.schemas import (
APIViewSchemaDescriptor, SchemaGenerator, get_schema_view
)
from rest_framework.test import APIClient, APIRequestFactory
from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet
@ -496,3 +499,16 @@ class Test4605Regression(TestCase):
'/auth/convert-token/'
])
assert prefix == '/'
class TestDescriptor(TestCase):
def test_apiview_schema_descriptor(self):
view = APIView()
assert hasattr(view, 'schema')
assert isinstance(view.schema, APIViewSchemaDescriptor)
def test_get_link_requires_instance(self):
descriptor = APIView.schema # Accessed from class
with pytest.raises(AssertionError):
descriptor.get_link(None, None, None) # ???: Do the dummy arguments require a tighter assert?