djangorestframework-hybridrouter


Namedjangorestframework-hybridrouter JSON
Version 1.0.2 PyPI version JSON
download
home_pageNone
SummaryDjango app that provides a hybrid router for Django Rest Framework
upload_time2024-12-12 12:15:08
maintainerNone
docs_urlNone
authorenzo_frnt
requires_python<4,>=3.8
licenseMIT
keywords django rest framework router hybrid views and viewsets
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage
            # djangorestframework-hybridrouter

A router for ViewSets and APIViews, with a better browsable API and support for nested routers!

***Inspired by [this topic](https://stackoverflow.com/questions/18817988/using-django-rest-frameworks-browsable-api-with-apiviews/78459183#78459183).***

## Overview

The `HybridRouter` class is an extension of Django REST Framework's `DefaultRouter` that allows you to register both `ViewSet`s and `APIView`s. It provides more flexibility in managing your URL routes, offers a better browsable API experience, and supports nested routers.

## Features

- Register both `ViewSet`s and `APIView`s using a unified interface.
- Simplified URL patterns for better readability.
- Automatic creation of intermediate API views for grouped endpoints (configurable).
- Support for nested routers.
- Automatic conflict resolution for basenames.

## Installation

```bash
pip install djangorestframework-hybridrouter
```

## Usage

Here’s an example of how to use the `HybridRouter`:

```python
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.viewsets import ViewSet
from django.urls import path, include
from hybridrouter import HybridRouter


class ServerConfigViewSet(ViewSet):
    def list(self, request):
        return Response({'a': 'b'})

class ServerConfigView(APIView):
    def get(self, request):
        return Response({'config': 'server'})

class ClientModsView(APIView):
    def get(self, request):
        return Response({'mods': 'client'})

class ServerModsView(APIView):
    def get(self, request):
        return Response({'mods': 'server'})

router = HybridRouter()
router.include_intermediate_views = True  # Enable intermediate API views

# Register APIViews
router.register('server-config', ServerConfigView, basename='server-config')
router.register('mods/client', ClientModsView, basename='mods-client')
router.register('mods/server', ServerModsView, basename='mods-server')

# Register a ViewSet
router.register('coucou', ServerConfigViewSet, basename='coucou')

# Register more APIViews under 'coucou' prefix
router.register('coucou/client', ClientModsView, basename='coucou-client')
router.register('coucou/server', ServerModsView, basename='coucou-server')

urlpatterns = [
    path('', include(router.urls)),
]
```

This configuration will generate URLs for both APIViews and ViewSets, and include intermediate API views for grouped endpoints.

## Documentation

**HybridRouter**

-   `register(prefix, view, basename=None)`

    Registers an `APIView` or `ViewSet` with the specified prefix.

    -   `prefix`: URL prefix for the view or viewset.
    -   `view`: The `APIView `or `ViewSet` class.
    -   `basename`: The base name for the view or viewset (optional). If not provided, it will be automatically generated.
-   `register_nested_router(prefix, router)`

    Registers a nested router under a specific prefix.

    -   `prefix`: URL prefix under which the nested router will be registered.
    -   `router`: The DRF router instance to be nested.

**Attributes**

-   `include_intermediate_views` (default True)

    Controls whether intermediate API views are automatically created for grouped endpoints. When set to True, the router will generate intermediate views that provide a browsable API listing of all endpoints under a common prefix.

**Notes**

-   Automatic Basename Conflict Resolution

    The `HybridRouter` automatically handles conflict resolution for `basenames`. If you register multiple `views` or `viewsets` with the same `basename`, it will assign unique `basenames` and log a warning.

-   Trailing Slash

    The `HybridRouter` uses a configurable trailing_slash attribute, defaulting to "/?" to match DRF’s `SimpleRouter` behavior.


## Advanced Features

### Custom Intermediary API Views

The `HybridRouter` can automatically create custom intermediary API views for grouped endpoints. This feature improves the organization of your API and provides a cleaner browsable interface.

**Example:**

```python
router = HybridRouter()
router.include_intermediate_views = True  # Enable intermediate API views

router.register('server-config', ServerConfigView, basename='server-config')
router.register('server-config/map', ServerConfigView, basename='server-config')
router.register('server-config/health', ServerConfigView, basename='server-config')
router.register('mods/client', ClientModsView, basename='mods-client')
router.register('mods/server', ServerModsView, basename='mods-server')
```

With `include_intermediate_views` set to True, the router will create intermediate views at the `mods/` prefix, providing a browsable API that lists both `client` and `server` endpoints under `mods/`. Also note that here for `server-config/` endpoints an intermediate view will not be created because it's already registered.

### Nested Routers

You can register nested routers under a specific prefix using `register_nested_router`. This allows you to include routers from other apps or create a complex URL structure.

**Example:**

```python
from rest_framework.routers import DefaultRouter

nested_router = DefaultRouter()
nested_router.register('items', ItemViewSet, basename='item')

router = HybridRouter()
router.register_nested_router('nested/', nested_router)

urlpatterns = [
    path('', include(router.urls)),
]
```


In this example, all routes from nested_router will be available under the `nested/` prefix.

## Experimental Features

The automatic creation of intermediary API views is a feature that improves the browsable API experience. This feature is still in development and may not work as expected in all cases. Please report any issues or suggestions.

```python
router = HybridRouter(enable_intermediate_apiviews=False)

router.register('server-config', ServerConfigView, name='server-config')
router.register('mods/client', ClientModsView, name='mods-client')
router.register('mods/server', ServerModsView, name='mods-server')
router.register('coucou/client', ClientModsView, name='coucou-client')
router.register('coucou/server', ServerModsView, name='coucou-server')
router.register('coucou', ServerConfigViewSet, basename='coucou')
```

With this configuration of the router with `enable_intermediate_apiviews`set to `False`, the intermediary API views will not be created. So the browsable API will look like on a `DefaultRouter` :

![image](./docs/imgs/Before.png)

But if you set `enable_intermediate_apiviews` to `True`, the intermediary API views will be created and the browsable API will look like this:

```python
router = HybridRouter(enable_intermediate_apiviews=True)
```

![image](./docs/imgs/After_1.png)
![image](./docs/imgs/After_2.png)

This improves the readability and the logic of the browsable API and provides a better user experience.

And as you can see that will not interfere with other already existing views. **Here, the `ServerConfigViewSet` is still accessible through the `coucou` endpoint and as not been overridden by an intermediary API view.**

## Testing

The package includes comprehensive tests to ensure reliability. Here are some highlights:

- Registering Views and ViewSets
Tests registering both APIViews and ViewSets, ensuring that URLs are correctly generated and accessible.

- Intermediate Views
Tests the creation of intermediate views when include_intermediate_views is enabled or disabled.

- Nested Routers
Tests registering nested routers and ensuring that their routes are correctly included under the specified prefix.

- Basename Conflict Resolution
Tests automatic conflict resolution when multiple views or viewsets are registered with the same basename.

## Notes

- Compatibility

    The HybridRouter is designed to work seamlessly with Django REST Framework and is compatible with existing DRF features like schema generation.

- Spectacular Support

    Will be added in the future. *After some manual test, it seems that the `spectacular` package is compatible with the `HybridRouter`. But that may need some more testing.*

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "djangorestframework-hybridrouter",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4,>=3.8",
    "maintainer_email": null,
    "keywords": "django, rest, framework, router, hybrid, views and viewsets",
    "author": "enzo_frnt",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/2f/5e/955f7ee262adfc3f047ab4730c51074ed7ea30fc53fbf2f74b1abcb9d809/djangorestframework_hybridrouter-1.0.2.tar.gz",
    "platform": null,
    "description": "# djangorestframework-hybridrouter\n\nA router for ViewSets and APIViews, with a better browsable API and support for nested routers!\n\n***Inspired by [this topic](https://stackoverflow.com/questions/18817988/using-django-rest-frameworks-browsable-api-with-apiviews/78459183#78459183).***\n\n## Overview\n\nThe `HybridRouter` class is an extension of Django REST Framework's `DefaultRouter` that allows you to register both `ViewSet`s and `APIView`s. It provides more flexibility in managing your URL routes, offers a better browsable API experience, and supports nested routers.\n\n## Features\n\n- Register both `ViewSet`s and `APIView`s using a unified interface.\n- Simplified URL patterns for better readability.\n- Automatic creation of intermediate API views for grouped endpoints (configurable).\n- Support for nested routers.\n- Automatic conflict resolution for basenames.\n\n## Installation\n\n```bash\npip install djangorestframework-hybridrouter\n```\n\n## Usage\n\nHere\u2019s an example of how to use the `HybridRouter`:\n\n```python\nfrom rest_framework.views import APIView\nfrom rest_framework.response import Response\nfrom rest_framework.viewsets import ViewSet\nfrom django.urls import path, include\nfrom hybridrouter import HybridRouter\n\n\nclass ServerConfigViewSet(ViewSet):\n    def list(self, request):\n        return Response({'a': 'b'})\n\nclass ServerConfigView(APIView):\n    def get(self, request):\n        return Response({'config': 'server'})\n\nclass ClientModsView(APIView):\n    def get(self, request):\n        return Response({'mods': 'client'})\n\nclass ServerModsView(APIView):\n    def get(self, request):\n        return Response({'mods': 'server'})\n\nrouter = HybridRouter()\nrouter.include_intermediate_views = True  # Enable intermediate API views\n\n# Register APIViews\nrouter.register('server-config', ServerConfigView, basename='server-config')\nrouter.register('mods/client', ClientModsView, basename='mods-client')\nrouter.register('mods/server', ServerModsView, basename='mods-server')\n\n# Register a ViewSet\nrouter.register('coucou', ServerConfigViewSet, basename='coucou')\n\n# Register more APIViews under 'coucou' prefix\nrouter.register('coucou/client', ClientModsView, basename='coucou-client')\nrouter.register('coucou/server', ServerModsView, basename='coucou-server')\n\nurlpatterns = [\n    path('', include(router.urls)),\n]\n```\n\nThis configuration will generate URLs for both APIViews and ViewSets, and include intermediate API views for grouped endpoints.\n\n## Documentation\n\n**HybridRouter**\n\n-   `register(prefix, view, basename=None)`\n\n    Registers an `APIView` or `ViewSet` with the specified prefix.\n\n    -   `prefix`: URL prefix for the view or viewset.\n    -   `view`: The `APIView `or `ViewSet` class.\n    -   `basename`: The base name for the view or viewset (optional). If not provided, it will be automatically generated.\n-   `register_nested_router(prefix, router)`\n\n    Registers a nested router under a specific prefix.\n\n    -   `prefix`: URL prefix under which the nested router will be registered.\n    -   `router`: The DRF router instance to be nested.\n\n**Attributes**\n\n-   `include_intermediate_views` (default True)\n\n    Controls whether intermediate API views are automatically created for grouped endpoints. When set to True, the router will generate intermediate views that provide a browsable API listing of all endpoints under a common prefix.\n\n**Notes**\n\n-   Automatic Basename Conflict Resolution\n\n    The `HybridRouter` automatically handles conflict resolution for `basenames`. If you register multiple `views` or `viewsets` with the same `basename`, it will assign unique `basenames` and log a warning.\n\n-   Trailing Slash\n\n    The `HybridRouter` uses a configurable trailing_slash attribute, defaulting to \"/?\" to match DRF\u2019s `SimpleRouter` behavior.\n\n\n## Advanced Features\n\n### Custom Intermediary API Views\n\nThe `HybridRouter` can automatically create custom intermediary API views for grouped endpoints. This feature improves the organization of your API and provides a cleaner browsable interface.\n\n**Example:**\n\n```python\nrouter = HybridRouter()\nrouter.include_intermediate_views = True  # Enable intermediate API views\n\nrouter.register('server-config', ServerConfigView, basename='server-config')\nrouter.register('server-config/map', ServerConfigView, basename='server-config')\nrouter.register('server-config/health', ServerConfigView, basename='server-config')\nrouter.register('mods/client', ClientModsView, basename='mods-client')\nrouter.register('mods/server', ServerModsView, basename='mods-server')\n```\n\nWith `include_intermediate_views` set to True, the router will create intermediate views at the `mods/` prefix, providing a browsable API that lists both `client` and `server` endpoints under `mods/`. Also note that here for `server-config/` endpoints an intermediate view will not be created because it's already registered.\n\n### Nested Routers\n\nYou can register nested routers under a specific prefix using `register_nested_router`. This allows you to include routers from other apps or create a complex URL structure.\n\n**Example:**\n\n```python\nfrom rest_framework.routers import DefaultRouter\n\nnested_router = DefaultRouter()\nnested_router.register('items', ItemViewSet, basename='item')\n\nrouter = HybridRouter()\nrouter.register_nested_router('nested/', nested_router)\n\nurlpatterns = [\n    path('', include(router.urls)),\n]\n```\n\n\nIn this example, all routes from nested_router will be available under the `nested/` prefix.\n\n## Experimental Features\n\nThe automatic creation of intermediary API views is a feature that improves the browsable API experience. This feature is still in development and may not work as expected in all cases. Please report any issues or suggestions.\n\n```python\nrouter = HybridRouter(enable_intermediate_apiviews=False)\n\nrouter.register('server-config', ServerConfigView, name='server-config')\nrouter.register('mods/client', ClientModsView, name='mods-client')\nrouter.register('mods/server', ServerModsView, name='mods-server')\nrouter.register('coucou/client', ClientModsView, name='coucou-client')\nrouter.register('coucou/server', ServerModsView, name='coucou-server')\nrouter.register('coucou', ServerConfigViewSet, basename='coucou')\n```\n\nWith this configuration of the router with `enable_intermediate_apiviews`set to `False`, the intermediary API views will not be created. So the browsable API will look like on a `DefaultRouter` :\n\n![image](./docs/imgs/Before.png)\n\nBut if you set `enable_intermediate_apiviews` to `True`, the intermediary API views will be created and the browsable API will look like this:\n\n```python\nrouter = HybridRouter(enable_intermediate_apiviews=True)\n```\n\n![image](./docs/imgs/After_1.png)\n![image](./docs/imgs/After_2.png)\n\nThis improves the readability and the logic of the browsable API and provides a better user experience.\n\nAnd as you can see that will not interfere with other already existing views. **Here, the `ServerConfigViewSet` is still accessible through the `coucou` endpoint and as not been overridden by an intermediary API view.**\n\n## Testing\n\nThe package includes comprehensive tests to ensure reliability. Here are some highlights:\n\n- Registering Views and ViewSets\nTests registering both APIViews and ViewSets, ensuring that URLs are correctly generated and accessible.\n\n- Intermediate Views\nTests the creation of intermediate views when include_intermediate_views is enabled or disabled.\n\n- Nested Routers\nTests registering nested routers and ensuring that their routes are correctly included under the specified prefix.\n\n- Basename Conflict Resolution\nTests automatic conflict resolution when multiple views or viewsets are registered with the same basename.\n\n## Notes\n\n- Compatibility\n\n    The HybridRouter is designed to work seamlessly with Django REST Framework and is compatible with existing DRF features like schema generation.\n\n- Spectacular Support\n\n    Will be added in the future. *After some manual test, it seems that the `spectacular` package is compatible with the `HybridRouter`. But that may need some more testing.*\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Django app that provides a hybrid router for Django Rest Framework",
    "version": "1.0.2",
    "project_urls": {
        "Documentation": "https://github.com/enzofrnt/djangorestframework-hybridrouter/blob/main/README.md",
        "Issues": "https://github.com/enzofrnt/djangorestframework-hybridrouter/issues",
        "Source Code": "https://github.com/enzofrnt/djangorestframework-hybridrouter"
    },
    "split_keywords": [
        "django",
        " rest",
        " framework",
        " router",
        " hybrid",
        " views and viewsets"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "8da98bd2730dc097d7e641f1a88616f1253ff238fe3fc5861ec1c08c968f0cfa",
                "md5": "dbacb1913d09afd6ba8015d9dbb659b7",
                "sha256": "90eff07858ea2dd4158862da15968b8a33d8f3cd471a267b2e31a0162e3d9405"
            },
            "downloads": -1,
            "filename": "djangorestframework_hybridrouter-1.0.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "dbacb1913d09afd6ba8015d9dbb659b7",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4,>=3.8",
            "size": 9158,
            "upload_time": "2024-12-12T12:15:06",
            "upload_time_iso_8601": "2024-12-12T12:15:06.133406Z",
            "url": "https://files.pythonhosted.org/packages/8d/a9/8bd2730dc097d7e641f1a88616f1253ff238fe3fc5861ec1c08c968f0cfa/djangorestframework_hybridrouter-1.0.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "2f5e955f7ee262adfc3f047ab4730c51074ed7ea30fc53fbf2f74b1abcb9d809",
                "md5": "479dfb6f91b9ebb6b3a0c26362031841",
                "sha256": "de8ac5791daa1edbefe6d62f1d30535dd7764a7ceb5e13ef100349b167e09805"
            },
            "downloads": -1,
            "filename": "djangorestframework_hybridrouter-1.0.2.tar.gz",
            "has_sig": false,
            "md5_digest": "479dfb6f91b9ebb6b3a0c26362031841",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4,>=3.8",
            "size": 10909,
            "upload_time": "2024-12-12T12:15:08",
            "upload_time_iso_8601": "2024-12-12T12:15:08.197921Z",
            "url": "https://files.pythonhosted.org/packages/2f/5e/955f7ee262adfc3f047ab4730c51074ed7ea30fc53fbf2f74b1abcb9d809/djangorestframework_hybridrouter-1.0.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-12-12 12:15:08",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "enzofrnt",
    "github_project": "djangorestframework-hybridrouter",
    "travis_ci": false,
    "coveralls": true,
    "github_actions": true,
    "tox": true,
    "lcname": "djangorestframework-hybridrouter"
}
        
Elapsed time: 0.77667s