Reputation: 3712
I am building a simple photos-albums app with Django Rest Framework (DRF). Each album has a UUID field in its model. I want to create 'share links' so that someone with a link of the form /albums/[uuid]
will be able to access that album.
I am using ModelViewSet
for my views, so I assume that the most succinct way to achieve this desired routing is via the action
decorator, with urls like /albums/shared/[uuid]
, but it's not clear to me how to obtain the uuid in the action-decorated shared
method. I can't even parse the URL because DRF will 404 before firing this shared
method:
### views/album.py
class AlbumViewSet(viewsets.ModelViewSet):
queryset = Album.objects.all()
serializer_class = AlbumSerializer
@action(detail=False, methods=['get'])
def shared(self, request):
# How do you get the uuid from the supplied URL?
uuid = ???
obj = self.get_queryset().objects.get(uuid=uuid)
return Response([obj])
Hopefully I won't have to add any fancy patterns to urls.py
, but if I do then here is what I have so far:
### myapp/urls.py
router = routers.DefaultRouter()
router.register(r'albums', AlbumViewSet)
...
urlpatterns = [
# ...
path('', include(router.urls)),
]
format_suffix_patterns(urlpatterns)
Thanks!
Upvotes: 2
Views: 2787
Reputation: 4634
Actions are great, but if you need to support multiple crud operations or ViewSet features like pagination or filtering, it's easier to map another ViewSet via router.register(...)
router.register(r'album', AlbumViewSet)
router.register(r'album/(?P<album_uuid>[\w-]+)/photo', PhotoViewSet)
class PhotoViewSet(ModelViewSet):
# Filter photos by album uid
def get_queryset(self):
qs = super().get_queryset()
return qs.filter(album_id=self.kwargs["album_uuid"])
Upvotes: 0
Reputation: 3712
The answer given by @Pradip is perfect but, for my own reference, here is a different solution I got working (except query part not tested!):
### myapp/views/album.py
class AlbumViewSet(viewsets.ModelViewSet):
...
@action(detail=False, methods=['get'],)
def shared(self, request, *args, **kwargs):
uuid = kwargs.get('uuid', None)
if uuid:
try:
obj = self.get_queryset().get(name=uuid)
return Response([obj])
except Album.DoesNotExist:
obj = None
raise exceptions.NotFound(f'Album with UUID {uuid} not found')
else:
raise exceptions.ParseError('UUID not parseable')
### myproject/urls.py
urlpatterns = [
...
path('albums/<str:uuid>/shared/', AlbumViewSet.as_view({'get': 'shared'})),
]
Upvotes: 0
Reputation: 2235
Here you need to some changes in url-pattern
and your action-decorater
:
First Change :
/albums/shared/[uuid]
to /albums/[uuid]/shared/
According to Docs.
Second Change :
class AlbumViewSet(viewsets.ModelViewSet):
queryset = Album.objects.all()
serializer_class = AlbumSerializer
⬇⬇⬇⬇
@action(detail=True, methods=['get'])
def shared(self,request,pk=None): ⬅⬅⬅⬅
uuid = pk
obj = self.get_queryset().objects.get(uuid=uuid)
return Response([obj])
Pass UUID in url
and fetch it in view
... For ex. /albums/45/shared/
Upvotes: 3