I have a ModelViewSet that defines several custom actions. I am using the default router in my urls.py to register the URLs. Right now, my views use the default created routes like ^images/$, ^images/{pk}/$.
In order for users to use the API using resource names with which they are familiar, I have adapted the viewset to accept multiple parameters from the URL, using the MultipleFieldLookupMixin strategy described in the docs to allow for the pattern images/{registry_host}/{repository_name}/{tag_name}.
I've created the get_object method in my viewset like so:
class ImageViewSet(viewsets.ModelViewSet):
...
def get_object(self):
special_lookup_kwargs = ['registry_host', 'repository_name', 'tag_name']
if all(arg in self.kwargs for arg in special_lookup_kwargs):
# detected the custom URL pattern; return the specified object
return Image.objects.from_special_lookup(**self.kwargs)
else: # must have received pk instead; delegate to superclass
return super().get_object()
I've also added a new URL path pattern for this:
urls.py
router = routers.DefaultRouter()
router.register(r'images', views.ImageViewSet)
# register other viewsets
...
urlpatterns = [
...,
path('images/<str:registry_host>/<path:repository_name>/<str:tag_name>', views.ImageViewSet.as_view({'get': 'retrieve',})),
path('', include(router.urls)),
]
The above all works as intended, however, I also have some extra actions in this model viewset:
@action(detail=True, methods=['GET'])
def bases(self, request, pk=None):
...
@action(detail=True, methods=['GET'])
def another_action(...):
... # and so on
With the default patterns registered by DRF, I could go to images/{pk}/<action> (like images/{pk}/bases) to trigger the extra action methods. However I cannot do this for images/{registry_host}/{repository_name}/{tag_name}/<action>. This is somewhat expected because I never registered any such URL and there's no reasonable way DRF could know about this.
I'm guessing that I can add all these paths manually with an appropriate arguments to path(...) but I'm not sure what that would be.
urlpatterns = [
...,
path('.../myaction', ???)
]
As a primary question, how do I add the actions for my URL?
However, I would like to avoid having to add new URLS every time a new @action() is added to the view. Ideally, I would like these to be registered automatically under the default path images/{pk}/<action> as well as images/{registry_host}/{repository_name}/{tag_name}/<action>.
As a secondary question, what is the appropriate way to achieve this automatically? My guess is perhaps a custom router. However, it's unclear to me how I would implement adding these additional routes for all extra (detail) actions.
Using django/drf 3.2
Another approach I see is to (ab)use url_path with kwargs like:
class ImageViewSet(viewsets.ModelViewSet):
@action(detail=False, methods=['GET'], url_path='<str:registry_host>/<path:repository_name>)/<str:tag_name>/bases')
def bases_special_lookup(...):
# detail=False to remove <pk>
@action(detail=True, methods=['GET'])
def bases(...):
...
@action(detail=False, methods=['GET'], url_path='<str:registry_host>/<path:repository_name>)/<str:tag_name>')
def retrieve_special_lookup(...):
# detail=False to remove <pk>
def retrieve(...):
...
This will create these urls:
images/<str:registry_host>/<path:repository_name>/<str:tag_name>/bases
images/<pk>/bases
images/<str:registry_host>/<path:repository_name>/<str:tag_name>
images/<pk>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With