So I will answer my own question. I took a look at DRF source code for schema generation. I came up with the plan and performed following steps.
I subclassed SchemaGenerator class defined in rest_framework.schemas module. Below is the code.
class CoreAPISchemaGenerator(SchemaGenerator):
def get_links(self, request=None, **kwargs):
links = LinkNode()
paths = list()
view_endpoints = list()
for path, method, callback in self.endpoints:
view = self.create_view(callback, method, request)
path = self.coerce_path(path, method, view)
paths.append(path)
view_endpoints.append((path, method, view))
if not paths:
return None
prefix = self.determine_path_prefix(paths)
for path, method, view in view_endpoints:
if not self.has_view_permissions(path, method, view):
continue
actions = getattr(view, 'actions', None)
schemas = getattr(view, 'schemas', None)
if not schemas:
link = view.schema.get_link(path, method, base_url=self.url)
subpath = path[len(prefix):]
keys = self.get_keys(subpath, method, view, view.schema)
insert_into(links, keys, link)
else:
action_map = getattr(view, 'action_map', None)
method_name = action_map.get(method.lower())
schema = schemas.get(method_name)
link = schema.get_link(path, method, base_url=self.url)
subpath = path[len(prefix):]
keys = self.get_keys(subpath, method, view, schema)
insert_into(links, keys, link)
return links
def get_keys(self, subpath, method, view, schema=None):
if schema and hasattr(schema, 'endpoint_name'):
return [schema.endpoint_name]
else:
if hasattr(view, 'action'):
action = view.action
else:
if is_list_view(subpath, method, view):
action = 'list'
else:
action = self.default_mapping[method.lower()]
named_path_components = [
component for component
in subpath.strip('/').split('/')
if '{' not in component
]
if is_custom_action(action):
if len(view.action_map) > 1:
action = self.default_mapping[method.lower()]
if action in self.coerce_method_names:
action = self.coerce_method_names[action]
return named_path_components + [action]
else:
return named_path_components[:-1] + [action]
if action in self.coerce_method_names:
action = self.coerce_method_names[action]
return named_path_components + [action]
I specifically modified two functions get_links and get_keys as that allow me to achieve what I wanted.
Further, for all the functions in viewsets that I was writing I dedicated an individual schema for it. I simply created a dictionary to keep mappings of function name to schema instance. For better approach I created a separate file to store schemas. For eg. if I had a viewset Clients I created a corresponding ClientsSchema class and within in defined staticmethods which returned schema instances.
Example,
In file where I am defining my schemas,
class ClientsSchema():
@staticmethod
def list_schema():
schema = schemas.ManualSchema(
fields=[],
description=''
)
schema.endpoint_name = 'Clients Listing'
return schema
In my apis.py,
class Clients(viewsets.ViewSet):
schemas = {
'list': ClientsSchema.list_schema()
}
def list(self, request, **kwargs):
pass
This setup allows me to define schemas for any type of functions that I add to my viewsets. In addition to it, I also wanted that the endpoints have an identifiable name and not the one generated by DRF which is like a > b > update > update.
In order to achieve that, I added endpoint_name property to schema object that is returned. That part is handled in get_keys function that is overridden.
Finally in the urls.py where we include urls for documentation we need to use our custom schema generator. Something like this,
urlpatterns.append(url(r'^livedocs/', include_docs_urls(title='My Services', generator_class=CoreAPISchemaGenerator)))
For security purposes I cannot share any snapshots. Apologies for that. Hope this helps.