Skip to content

Multiple @actions with same URL path and different methods (or alternately: @detail operation IDs) #7683

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
4 of 6 tasks
akx opened this issue Jan 4, 2021 · 2 comments
Closed
4 of 6 tasks

Comments

@akx
Copy link
Contributor

akx commented Jan 4, 2021

Checklist

  • I have verified that that issue exists against the master branch of Django REST framework.
  • I have searched for similar issues in both open and closed tickets and cannot find a duplicate.
  • This is not a usage question. (It sort of isn't...)
  • This cannot be dealt with as a third party library.
  • I have reduced the issue to the simplest possible case.
  • I have included a failing test as a pull request. (If you are unable to do so we can still accept the issue.)

Description

Following an upgrade DRF 3.11.2 -> 3.12.2, in order to avoid the warning about duplicate OpenAPI Operation IDs (implemented in #7207), I had to refactor extra ViewSet actions such as

    @action(methods=['PATCH', 'DELETE'], url_path='members', detail=True)
    def members(self, request):
        # ...

that generates two OpenAPI operations, both called SomethingMembers, to

    @action(methods=['PATCH'], url_path='members', detail=True)
    def edit_member(self, request, **kwargs):
        return self._handle_members(request)

    @action(methods=['DELETE'], url_path='members', detail=True)
    def delete_member(self, request, **kwargs):
        return self._handle_members(request)

    def _handle_members(self, request):

that generates two OpenAPI operations, called SomethingEditMember and SomethingDeleteMember and doesn't warn about things.

However, this screws up routing, since both views have the same URL path, and as such one of them gets selected by Django when dispatching the request, which naturally leads to Method Not Alloweds when attempting to use the one not selected.

Resolutions

  • I suppose it could be possible to rework the Router to have an internal per-method dispatch when it notices an URL regex is used by two "destinations" with different methods? (This admittedly sounds brittle and complex.)
  • The easier way might be to somehow easily allow setting an operation_id for @actions too (which is what I'll be looking into next, probably by way of hacking the Schema generator somehow since there's no official support I can see).
@akx
Copy link
Contributor Author

akx commented Jan 4, 2021

Okay, yeah, this was fixed with a decorator like

def action_operation_id_suffixes(method_map: Dict[str, str]):
    """
    Anoint the given `@action` function with a mapping of method -> OpenAPI operation ID.
    """
    def decorator(fn):
        fn.operation_id_suffixes = {
            method.lower(): suffix
            for (method, suffix)
            in method_map.items()
        }
        return fn

    return decorator

which is used e.g.

    @action(methods=['POST', 'PATCH', 'DELETE'], detail=True)
    @action_operation_id_suffixes({
        'post': 'AddMember',
        'patch': 'EditMember',
        'delete': 'DeleteMember',
    })
    def members(self, request, **kwargs):

and with a hack to our already customized schema generator:

        if hasattr(func, 'operation_id_suffixes'):
            action = func.operation_id_suffixes.get(method.lower(), action)

Still, might be nice to have this as a firster-party feature...

@akx akx closed this as completed Jan 4, 2021
@sshishov
Copy link
Contributor

sshishov commented Mar 14, 2023

For someone googling, this is the comment where the issue has been fixed, and from there you can get how to do it: #4920 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants