Метод запрещен (PUT) при использовании Django Rest в паре с интерфейсом Vue.js
Я пытаюсь сделать запрос PUT к моему RestAPI с помощью Vue.js. Он полностью работает в моем представлении ModelViewSet, но я сталкиваюсь с запрещенным методом (PUT), когда пробую его через Vue.js. Я также могу использовать "POST" в той же функции onSubmit. Вот код функции onSubmit в моем QuestionEditor.vue:
<template>
<div class="container mt-2">
<h1 class="mb-3">Ask a Question</h1>
<form @submit.prevent="onSubmit">
<textarea v-model="question_body" class="form-control" placeholder="What do you want to ask?" row="3">
</textarea>
<br>
<button type="submit" class="btn btn-success">
Publish
</button>
</form>
<p v-if="error" class="muted mt-2">{{ error }}</p>
</div>
</template>
<script>
import { apiService } from "../common/api.service.js"
export default {
name: "QuestionEditor",
props: {
slug: {
type: String,
required: null
}
},
data() {
return{
question_body: null,
error: null
}
},
methods: {
onSubmit() {
if (!this.question_body){
this.error = "You can't send an empty question!";
} else if (this.question_body.length > 240) {
this.error = "Exceeding 240-character limit!";
} else {
let endpoint = "/api/questions/";
let method = "POST";
if (this.slug !== undefined) {
endpoint += `${ this.slug }/`;
method = "PUT";
}
apiService(endpoint, method, { content: this.question_body })
.then(question_data => {
this.$router.push({
name: 'Question',
params: {slug: question_data.slug}
})
})
}
}
},
async beforeRouteEnter(to, from, next){
if (to.params.slug !== undefined) {
let endpoint = `/api/questions/${to.params.slug}/`;
let data = await apiService(endpoint);
return next(vm => (vm.question_body = data.content));
} else{
return next();
}
},
created() {
document.title = "Editor - QuestionTime";
}
}
</script>
<style scoped>
</style>
и вот функция apiService, используемая для получения данных из API:
import { CSRF_TOKEN } from "./csrf_token.js"
async function getJson(response) {
if (response.status === 204) return '';
return response.json()
}
function apiService(endpoint, method, data) {
const config = {
method: method || "GET",
body: data !== undefined ? JSON.stringify(data) : null,
headers: {
'content-type': 'application/json',
'X-CSRFTOKEN': CSRF_TOKEN
}
};
return fetch(endpoint, config)
.then(getJson)
.catch(error => console.log(error))
}
export { apiService };
и, наконец, представление api:
class QuestionViewSet(viewsets.ModelViewSet):
queryset = Question.objects.all().order_by("-created_at")
lookup_field = "slug"
serializer_class = QuestionSerializer
permission_classes = [IsAuthenticated, IsAuthorOrReadOnly]
def perform_create(self, serializer):
serializer.save(author=self.request.user)
обновление: сериализатор вопросов:
class QuestionSerializer(serializers.ModelSerializer):
author = serializers.StringRelatedField(read_only=True)
created_at = serializers.SerializerMethodField(read_only=True)
slug = serializers.SlugField(read_only=True)
answers_count = serializers.SerializerMethodField()
user_has_answered = serializers.SerializerMethodField()
class Meta:
model = Question
exclude = ["updated_at"]
def get_created_at(self, instance):
return instance.created_at.strftime("%B %d %Y")
def get_answers_count(self, instance):
return instance.answers.count()
def get_user_has_answered(self, instance):
request = self.context.get("request")
return instance.answers.filter(author=request.user).exists()
и url.py для api:
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from .views import QuestionViewSet, AnswerCreateAPIView, AnswerListAPIView, AnswerRUDAPIView, AnswerLikeAPIView
router = DefaultRouter()
router.register(r"questions", QuestionViewSet)
urlpatterns = [
path("", include(router.urls)),
path("questions/<slug:slug>/answer/", AnswerCreateAPIView.as_view(), name="answer-create"),
path("questions/<slug:slug>/answers/", AnswerListAPIView.as_view(), name="answer-list"),
path("answers/<int:pk>/", AnswerRUDAPIView.as_view(), name="answer-detail"),
path("answers/<int:pk>/like/", AnswerLikeAPIView.as_view(), name="answer-like"),
]
Update2: файл разрешений:
from rest_framework import permissions
class IsAuthorOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.author == request.
Решено!!! Я понизил альфа-версию web-pack версии 1 до 0.4.3, и теперь все работает нормально. Я совершил ошибку, выбрав полуразработанную версию.
3 ответа
Думаю, теперь я могу понять, что происходит.
Вам либо нужно добавить поле pk в сериализатор (
fields
вMeta
должен иметьid
поле)или ваша конечная точка неверна.
} else {
let endpoint = "api/questions/";
let method = "POST";
if (this.slug !== undefined) {
endpoint += `${ this.slug }/`;
method = "PUT";
}
apiService(endpoint, method, { content: this.question_body })
.then(question_data => {
this.$router.push({
name: 'Question',
params: {slug: question_data.slug}
})
})
Если все, что вы здесь делаете, это изменение метода, этого недостаточно, так как вы отправляете POST на /resource
но ПОСТАВИТЬ на /resource/<id>
.
Следовательно, PUT в /resource
не допускается.
Вы можете переопределить эту логику в ModelViewSet
взять id
от тела, но я бы не рекомендовал это делать.
РЕДАКТИРОВАТЬ: я только что заметил, что вы добавили endpoint += ${ this.slug }/;
. Не могли бы вы показать, как здесь выглядит ваша конечная точка для PUT? С этой пулей. Это правильный идентификатор в вашем сериализаторе?
Я думаю, вам нужно разрешить заголовки cors на стороне вашего сервераpip install django-cors-headers
в приложении django settings.py:
INSTALLED_APPS = [
'corsheaders',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
]
CORS_ORIGIN_ALLOW_ALL = True
Похоже, у вас проблема с разрешениями
permission_classes = [IsAuthenticated, IsAuthorOrReadOnly]
Я предполагаю, что ты IsAuthorOrReadOnly
вызывает это. Не могли бы вы предоставить его исходный код?