Django: 'Метод \"PATCH\" не разрешен.' 405 после добавления внешнего ключа

Я делаю проект для университета и решил сделать его на Django, чтобы научиться чему-то новому. Я начинаю жалеть об этом... и надеюсь, что вы сможете помочь.

Я создаю простой API, который раньше работал хорошо, но после добавления связанного класса в модель, он позволяет мне выполнять только операции GET & POST, и я не знаю, почему это происходит и как это исправить - я указал http методы в наборах представлений. Я хочу также выполнять операции PUT, PATCH & DELETE.

models.py:

from django.db import models

class Author(models.Model):
    id = models.AutoField(primary_key=True)
    first_name = models.CharField(max_length=60,blank=True,null=True)
    last_name = models.CharField(max_length=60,blank=True,null=True)
    description = models.CharField(max_length=1000,blank=True,null=True)

    def __str__(self):
        return "%s %s" % (self.first_name, self.last_name)

class Quote(models.Model):
    id = models.AutoField(primary_key=True)
    content = models.CharField(max_length=1000)
    author = models.ForeignKey(Author, on_delete=models.SET_NULL,related_name='quotes',null=True)
    source = models.CharField(max_length=60,blank=True,null=True)
    context = models.CharField(max_length=1000,blank=True,null=True)
    year = models.IntegerField(blank=True,null=True)

    def __str__(self):
        return self.content

serializers.py:

from rest_framework import serializers

from .models import Quote, Author

class QuoteSerializer(serializers.ModelSerializer):

    class Meta:
        model = Quote
        fields = ('id','content','author','source','year','context')
        lookup_field = 'author'

class AuthorSerializer(serializers.HyperlinkedModelSerializer):
    quotes = QuoteSerializer(many=True, read_only=True)

    class Meta:
        model = Author
        fields = ('id','first_name','last_name','description','quotes')

urls.py:

from django.urls import include, path
from rest_framework import routers
from . import views

router = routers.DefaultRouter()
router.register(r'authors', views.AuthorViewSet,basename='authors')
router.register(r'quotes', views.QuoteViewSet,basename='quotes')

urlpatterns = [
    path('', include(router.urls)),
    path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]

views.py:

from rest_framework import viewsets
from rest_framework.exceptions import NotFound
from rest_framework.response import Response
from rest_framework import status

from .serializers import QuoteSerializer, AuthorSerializer
from .models import Quote, Author

class AuthorViewSet(viewsets.ModelViewSet):
    http_method_names = ['get', 'put', 'post','patch', 'delete']
    serializer_class = AuthorSerializer

    def get_queryset(self):
        queryset = Author.objects.all().order_by('id')
        id = self.request.query_params.get('id')
        first_name = self.request.query_params.get('first_name')
        last_name = self.request.query_params.get('last_name')
        if id is not None:
            queryset = queryset.filter(id=id)
        if first_name is not None:
            queryset = queryset.filter(first_name=first_name)
        if last_name is not None:
            queryset = queryset.filter(last_name=last_name)
        if queryset:
            return queryset
        else:
            raise NotFound

    def destroy(self, request, *args, **kwargs):
        obj = self.get_object()
        self.perform_destroy(obj)
        return Response(data={'detail': "Deleted successfully"},status=status.HTTP_204_NO_CONTENT)

class QuoteViewSet(viewsets.ModelViewSet):
    http_method_names = ['get', 'put', 'post','patch', 'delete']
    serializer_class = QuoteSerializer
    lookup_field = 'author'

    def get_queryset(self):
        queryset = Quote.objects.all().order_by('id')
        id = self.request.query_params.get('id')
        author = self.request.query_params.get('author')
        source = self.request.query_params.get('source')
        year = self.request.query_params.get('year')
        if id is not None:
            queryset = queryset.filter(id=id)
        if author is not None:
            queryset = queryset.filter(author=author)
        if source is not None:
            queryset = queryset.filter(source=source)
        if year is not None:
            queryset = queryset.filter(year=year)
        if queryset:
            return queryset
        else:
            raise NotFound

    def destroy(self, request, *args, **kwargs):
        obj = self.get_object()
        self.perform_destroy(obj)
        return Response(data={'detail': "Deleted successfully"},status=status.HTTP_204_NO_CONTENT)

Насколько я знаю, когда вы используете ModelViewSet, у вас есть 5 разрешенных методов, list(), retrieve(), create(), update(), destroy(), и queryset, который считается как GET, который вы всегда должны определять. Я вижу, что вы хотите использовать миксины (perform_destroy-DestroyModelMixin). Это не обязательно, у вас есть все, чтобы обойтись без этого.

  1. list(self, request)->GET, create(self, request)->POST.

вы должны указать идентификатор для них:

  1. retrieve()->GET, destroy()->DELETE, update() -> PUT

не нужна эта переменная 'http_method_names', когда вы используете postman, определите, какой метод хотите выполнить.

Ваши методы должны выглядеть следующим образом.

def destroy(self, request, pk=None, *args,**kwargs):
    obj = self.model.objects.filter(id=pk)
    if obj:`enter code here`
       obj.delete()
       return Response({'detail':'Deleted Succesfully'}, status=status.HTTP_204_NO_CONTENT)
    return Response({'error':'record doesn't exists'}, status=status.HTTP_400_BAD_REQUEST)

В POSTMAN МЕТОД:УДАЛЕНИЕ URL: http://localhost/autors/1/

То же самое и с retrieve - он просто показывает информацию об одном объекте. create и list не нуждаются в id, потому что вы знаете :D Надеюсь, это было полезно, я тоже учусь =)

Если кому-то интересно - проблема была в поле поиска в QuoteViewset, которое создавало проблему с гиперссылкой моего API. После удаления она работает нормально.

Вернуться на верх