Wyłącz metodę w ViewSet, django-rest-framework

124

ViewSets mieć automatyczne metody wyświetlania, pobierania, tworzenia, aktualizowania, usuwania, ...

Chciałbym wyłączyć niektóre z nich, a rozwiązanie, które wymyśliłem, prawdopodobnie nie jest dobre, ponieważ OPTIONSnadal określa je jako dozwolone.

Masz jakiś pomysł, jak to zrobić we właściwy sposób?

class SampleViewSet(viewsets.ModelViewSet):
    queryset = api_models.Sample.objects.all()
    serializer_class = api_serializers.SampleSerializer

    def list(self, request):
        return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED)
    def create(self, request):
        return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED)
db0
źródło

Odpowiedzi:

250

Definicja ModelViewSetto:

class ModelViewSet(mixins.CreateModelMixin, 
                   mixins.RetrieveModelMixin, 
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet)

Więc zamiast rozszerzać ModelViewSet, dlaczego nie użyć tego, czego potrzebujesz? Na przykład:

from rest_framework import viewsets, mixins

class SampleViewSet(mixins.RetrieveModelMixin,
                    mixins.UpdateModelMixin,
                    mixins.DestroyModelMixin,
                    viewsets.GenericViewSet):
    ...

Przy takim podejściu router powinien generować trasy tylko dla uwzględnionych metod.

Odniesienie :

ModelViewSet

SunnySydeUp
źródło
@SunnySydeUp Próbuję tego teraz i wygląda na to, że router generuje trasę dla widoku listy, ale 404s, ponieważ ViewSet nie wie, jak obsłużyć żądanie. Czy tego się spodziewałeś?
Steve Jalim
3
Używając tylko potrzebnych mikserów, możesz wyłączyć metody GET, POST, PUT, DELETE, ale nie byłem w stanie dowiedzieć się, jak wyłączyć metodę PATCH, szczególnie jeśli używasz routerów.
Muneeb Ahmad
3
@MuneebAhmad Metoda PATCH jest włączona z poziomu UpdateModelMixin. Jeśli chcesz użyć aktualizacji, ale nie łatki, mogę obecnie wymyślić dwa sposoby. Możesz albo nadpisać dozwolone metody w widoku i usunąć „poprawkę”, albo możesz nadpisać partial_updatemetodę i wywołanie http_method_not_allowed(request, *args, **kwargs). Nie testowałem tego, więc nie jestem pewien, czy to działa
SunnySydeUp
1
@JulioMarins Dodałem odniesienie. Nie jestem jednak pewien, czy tego chciałeś.
SunnySydeUp
1
Jeśli ktoś chce utworzyć zestaw widoków tylko do odczytu, może użyć class SampleViewSet(viewsets.ReadOnlyModelViewSet).
Bikash kharel
133

Możesz nadal używać viewsets.ModelViewSeti definiować http_method_namesw swoim ViewSet.

Przykład

class SampleViewSet(viewsets.ModelViewSet):
    queryset = api_models.Sample.objects.all()
    serializer_class = api_serializers.SampleSerializer
    http_method_names = ['get', 'post', 'head']

Po dodaniu http_method_names, nie będzie w stanie to zrobić puti patchjuż.

Jeśli chcesz, putale nie chcesz patch, możesz zatrzymaćhttp_method_names = ['get', 'post', 'head', 'put']

Wewnętrznie widoki DRF rozciągają się z Django CBV. Django CBV ma atrybut o nazwie http_method_names. Możesz więc używać http_method_names również z widokami DRF.

[Shameless Plug]: Jeśli ta odpowiedź była pomocna, spodoba ci się moja seria postów na temat DRF pod adresem https://www.agiliq.com/blog/2019/04/drf-polls/ .

Akshar Raaj
źródło
16
Problem z tym sposobem nie polega na wyłączeniu żadnej listy ani pobierania. Muszę wyłączyć oba lub żadne
Fuad
1
To nie zadziałało, po uwzględnieniu get and head nadal byłem w stanie zrobić post
RunLoop
To działa dla mnie na django 1.9. Świetne rozwiązanie. Czy istnieje ryzyko, że użytkownicy mogą wykonać żądanie GET w inny sposób?
Ycon
FANTASTYCZNE rozwiązanie. Działa python3i Django 1.10po prostu dobrze.
Urda
2
Wolę to podejście, ponieważ nie mogłem zmienić dziedziczenia miksów, aby uwzględnić PATCH, ale nie PUT, ponieważ oba są implementacją mixins.UpdateModelMixin
ThatsAMorais
5

Chociaż minęło trochę czasu dla tego posta, nagle dowiedziałem się, że tak naprawdę jest to sposób na wyłączenie tych funkcji, możesz go edytować bezpośrednio w views.py.

Źródło: https://www.django-rest-framework.org/api-guide/viewsets/#viewset-actions

from rest_framework import viewsets, status
from rest_framework.response import Response

class NameWhateverYouWantViewSet(viewsets.ModelViewSet):

    def create(self, request):
        response = {'message': 'Create function is not offered in this path.'}
        return Response(response, status=status.HTTP_403_FORBIDDEN)

    def update(self, request, pk=None):
        response = {'message': 'Update function is not offered in this path.'}
        return Response(response, status=status.HTTP_403_FORBIDDEN)

    def partial_update(self, request, pk=None):
        response = {'message': 'Update function is not offered in this path.'}
        return Response(response, status=status.HTTP_403_FORBIDDEN)

    def destroy(self, request, pk=None):
        response = {'message': 'Delete function is not offered in this path.'}
        return Response(response, status=status.HTTP_403_FORBIDDEN)
W Kenny
źródło
Powinien to być preferowany sposób.
digitake
Myślę, że HTTP_400_BAD_REQUEST byłby bardziej odpowiedni tutaj, jeśli nie jest związany z auth.
Santiago Magariños
4

Jeśli próbujesz wyłączyć metodę PUT z zestawu widoków DRF, możesz utworzyć niestandardowy router:

from rest_framework.routers import DefaultRouter

class NoPutRouter(DefaultRouter):
    """
    Router class that disables the PUT method.
    """
    def get_method_map(self, viewset, method_map):

        bound_methods = super().get_method_map(viewset, method_map)

        if 'put' in bound_methods.keys():
            del bound_methods['put']

        return bound_methods

Po wyłączeniu metody na routerze dokumentacja schematu API będzie poprawna.

storn
źródło
Ponieważ częściowa łatka nie jest poprawnie zaimplementowana w DRF, rozsądnie byłoby usunąć ją globalnie w sposób opisany tutaj
oden
1

Jak wyłączyć metodę „DELETE” dla ViewSet w DRF

class YourViewSet(viewsets.ModelViewSet):
    def _allowed_methods(self):
        return [m for m in super(YourViewSet, self)._allowed_methods() if m not in ['DELETE']]

PS Jest to bardziej niezawodne niż jawne określenie wszystkich niezbędnych metod, więc istnieje mniejsze prawdopodobieństwo zapomnienia niektórych ważnych metod OPCJE, GŁOWA itp.

PPS domyślnie ma DRF http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

pymen
źródło
Nie możesz zadzwonić superna poziomie klasy, nie ma self.
validname
0

W Django Rest Framework 3.xx możesz po prostu włączyć każdą metodę, dla której chcesz być włączona ModelViewSet, przekazując słownik do as_viewmetody. W tym słowniku klucz musi zawierać typ żądania (GET, POST, DELETE itp.), A wartość musi zawierać odpowiednią nazwę metody (lista, pobieranie, aktualizacja itp.). Na przykład powiedzmy, że chcesz, aby Samplemodel został utworzony lub odczytany, ale nie chcesz, aby był on modyfikowany. Więc to oznacza, że chcesz list, retrievea createmetoda się włączyć (i chcesz innym być wyłączone).

Wszystko, co musisz zrobić, to dodać takie ścieżki urlpatterns:

path('sample/', SampleViewSet.as_view({
    'get': 'list',
    'post': 'create'
})),
path('sample/<pk>/', SampleViewSet.as_view({  # for get sample by id.
    'get': 'retrieve'
}))

Jak widać, w powyższych ustawieniach routingu nie ma żądania deletei putżądania, więc na przykład, jeśli wyślesz putżądanie na adres URL, odpowie Ci 405 Method Not Allowed:

{
    "detail": "Method \"PUT\" not allowed."
}
Hamidreza
źródło