【问题标题】:Generate schema for Django rest framework viewset actions为 Django 休息框架视图集操作生成模式
【发布时间】:2019-04-08 20:10:35
【问题描述】:

根据 DRF 文档,我开始使用 ViewSet 并实现了list, retrieve, create, update and destroyactions。我有另一个 APIView,我可以为其编写架构 (ManualSchema),当我导航到 /docs/ 时,我可以访问文档以及进行交互的实时端点。

我希望为每个视图集操作创建单独的架构。我试着写了一个,但它没有显示出来,所以我想我错过了一些东西。

代码如下:

class Clients(viewsets.ViewSet):

    '''

        Clients is DRF viewset which implements `create`, `update`, `read` actions by implementing create, update, list and retrieve functions respectively.

    '''
    list_schema = schemas.ManualSchema(fields=[
            coreapi.Field(
                'status',
                required=False,
                location='query',
                description='Accepted values are `active`, `inactive`'
            ),          
        ], 
        description='Clients list',
        encoding='application/x-www-form-urlencoded')

    @action(detail=True, schema=list_schema)
    def list(self, request):

        '''Logic for listing'''


    def retrieve(self, request, oid=None):

        '''Logic for retrieval'''


    create_schema = schemas.ManualSchema(fields=[
            coreapi.Field(
                'name',
                required=False,
                location='body',
            ),
            coreapi.Field(
                'location',
                required=False,
                location='body',
            ),              
        ], 
        description='Clients list',
        encoding='application/x-www-form-urlencoded')

    @action(detail=True, schema=create_schema)
    def create(self, request):

        '''Logic for creation'''

【问题讨论】:

    标签: django django-rest-framework core-api


    【解决方案1】:

    所以我会回答我自己的问题。我查看了用于模式生成的 DRF 源代码。我想出了计划并执行了以下步骤。

    我继承了 rest_framework.schemas 模块中定义的 SchemaGenerator 类。下面是代码。

    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]
    

    我专门修改了两个函数 get_linksget_keys 让我可以实现我想要的。

    此外,对于我正在编写的视图集中的所有函数,我专门为其创建了一个单独的模式。我只是创建了一个字典来保存函数名称到模式实例的映射。为了更好的方法,我创建了一个单独的文件来存储模式。例如。如果我有一个视图集Clients,我创建了一个相应的ClientsSchema 类,并在定义的返回模式实例的静态方法中。

    例子,

    在我定义架构的文件中,

    class ClientsSchema():
    
        @staticmethod
        def list_schema():
    
            schema = schemas.ManualSchema(
                fields=[],
                description=''
            )
    
            schema.endpoint_name = 'Clients Listing'
    
            return schema
    

    在我的 apis.py 中,

    class Clients(viewsets.ViewSet):
    
        schemas = {
            'list': ClientsSchema.list_schema()
        }
    
        def list(self, request, **kwargs):
            pass
    

    此设置允许我为添加到视图集中的任何类型的函数定义模式。除此之外,我还希望端点具有可识别的名称,而不是由 DRF 生成的名称,例如 a > b > update > update。 为了实现这一点,我将endpoint_name 属性添加到返回的schema 对象中。该部分在被覆盖的get_keys 函数中处理。

    最后在 urls.py 中,我们包含了我们需要使用自定义模式生成器的文档的 url。像这样的,

    urlpatterns.append(url(r'^livedocs/', include_docs_urls(title='My Services', generator_class=CoreAPISchemaGenerator)))
    

    出于安全考虑,我不能分享任何快照。对此表示歉意。希望这会有所帮助。

    【讨论】:

      【解决方案2】:

      我认为您尝试做的事情是不可能的。 ViewSetdoes not provide any method handlers,因此,您不能在方法 createlist 上使用 @action 装饰器,因为它们是现有路由。

      【讨论】:

      • 我想也没有解决方法,或者有没有?
      • 我不这么认为。
      • 我很想克隆 drf/coreapi 项目并让它工作:D
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-08
      • 1970-01-01
      • 2022-12-19
      相关资源
      最近更新 更多