I'm trying to build a single page app (Vue) with a django backend. It's a very simple app to practice Vue + Django Rest Framework. The app allows the user to creating, delete and view (list) stories (just a title, datetime and content (simply text)) and this works fine. I'm now trying to also implement editing capabilities, but I get an error with regards to CORS headers.
My backend code is very simple. Settings.py
# relevant parts of settings.py
INSTALLED_APPS = [
...,
'rest_framework',
'corsheaders'
]
MIDDLEWARE = [
...,
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
]
CORS_ALLOWED_ORIGINS = [
"http://localhost:8080",
]
CORS_ALLOW_METHODS = (
'GET',
'POST',
'PUT',
'PATCH',
'DELETE',
'OPTIONS'
)
urls.py:
urlpatterns = [
path('', views.StoryList.as_view(), name='stories'),
path('<int:pk>/', views.StoryDetail.as_view(), name='story'),
path('delete/<int:pk>/', views.StoryDetail.as_view(), name='delete_story'),
path('update/<int:pk>/', views.StoryDetail.as_view(), name='update_story')
]
views.py:
from django.shortcuts import render
from django.http import HttpResponse, Http404
from rest_framework import generics
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import Story
from .serializers import StorySerializer
class StoryList(APIView):
def get(self, request, format=None):
stories = Story.objects.all()
serializer = StorySerializer(stories, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = StorySerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.data, status=status.HTTP_400_BAD_REQUEST)
class StoryDetail(APIView):
def get_object(self, pk):
try:
return Story.objects.get(pk=pk)
except Story.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
story = self.get_object(pk)
serializer = StorySerializer(story)
return Response(serializer.data)
def delete(self, request, pk, format=None):
story = self.get_object(pk)
story.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
def update(self, request, pk, *args, **kwargs):
story = self.get_object(pk)
print(story)
serializer = StorySerializer(story)
if serializer.is_valid():
serializer.save()
return Response(status=status.HTTP_201_CREATED, data=serializer.data)
return Response(status=status.HTTP_400_BAD_REQUEST, data='Wrong parameters')
So the methods get and delete work fine on the detail view. And list and create on the listview also work fine. Only the update method doesn't work. The error I get:
405 Method Not Allowed
The response header indeed does not define it as allowed:
Allow: GET, DELETE, HEAD, OPTIONS
But I thought I had defined everything in settings.py
My frontend code in case necessary (relevant parts):
<template>
<form
id="add_story"
action="http://localhost:8000"
method="post"
@submit.prevent="saveStory">
<input
id="story_id"
type="hidden"
v-model="value.id"
required />
<input
id="story_datetime"
type="hidden"
v-model="value.datetime"
required />
<b-form-input
id="story_title"
v-model="value.title"
required>
</b-form-input>
<b-form-textarea
id="story"
v-model="value.content">
</b-form-textarea>
<b-button type="submit" variant="primary">Submit</b-button>
</form>
</template>
<script>
export default {
name: 'Story-form',
props: {
// gets the data from the parent which works fine
value: {
type: Object,
required: true
},
},
methods: {
async saveStory () {
// if value.id is set: update story
const now = new Date();
if (this.value.datetime == ""){
this.value.datetime = now.toISOString();
}
const story_data = {
// id: this.value.id,
title: this.value.title,
content: this.value.content,
datetime: this.value.datetime
};
let send_method = (story_data.id === null) ? "POST" : "PUT";
let send_url = (story_data.id === null) ? "http://localhost:8000/" : "http://localhost:8000/update/" + String(this.value.id) + "/";
await fetch(send_url, {
method: send_method,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(story_data)
}).then( (response) => {
if (!response.ok){
console.log(response);
this.error = false;
} else {
// reset everything back to normal
}
}).catch(e => {
console.log(e);
});
}
}
}
</script>
Any idea on how to solve this. I'd prefer not to use viewsets because I'm trying to understand DRF and with viewsets too much happens under the hood.