Django Rest Framework: как направить uuid к связанному объекту?
Я создаю простое приложение для фотографий-альбомов с помощью Django Rest Framework (DRF). Каждый альбом имеет поле UUID в своей модели. Я хочу создать 'share links', чтобы кто-то со ссылкой вида /albums/[uuid] мог получить доступ к этому альбому.
Я использую ModelViewSet для своих представлений, поэтому я предполагаю, что наиболее лаконичный способ достижения желаемой маршрутизации - это декоратор action, с урлами типа /albums/shared/[uuid], но мне не ясно, как получить uuid в методе shared, декорированном действием. Я даже не могу разобрать URL, потому что DRF будет 404 перед тем, как запустить этот метод shared:
### 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])
Надеюсь, мне не придется добавлять никаких причудливых узоров к urls.py, но если придется, то вот что у меня есть на данный момент:
### myapp/urls.py
router = routers.DefaultRouter()
router.register(r'albums', AlbumViewSet)
...
urlpatterns = [
# ...
path('', include(router.urls)),
]
format_suffix_patterns(urlpatterns)
Спасибо!
Здесь вам необходимо внести некоторые изменения в url-pattern и ваш action-decorater:
Первое изменение :
/albums/shared/[uuid] к /albums/[uuid]/shared/ Согласно Docs.
Второе изменение :
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])
Передайте UUID в url и получите его в view... Например. /albums/45/shared/
Ответ, данный @Pradip, идеален, но для справки, вот другое решение, которое у меня работает (кроме части запроса, не проверенной!):
### 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'})),
]
Акции - это здорово, но если вам нужно поддерживать несколько грубых операций или функции ViewSet, такие как пагинация или фильтрация, проще отобразить другой ViewSet через 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"])