[英]Django/DRF backend not receiving complete data from POST request
我正在嘗試通過 Axios POST 請求將數據發送到我的 Django/DRF 后端。 他們中的一些人一直在工作,但有些人還沒有工作。 例如,這個片段可以正常工作:
axiosInstance.post(
`/api/notes/`,
{
title,
content: serialize(editorContent),
}
)
然而,這個問題一直在引起問題:
axiosInstance.post(
`/api/notebooks/`,
{
name: newNotebookName,
user,
}
)
在第二個片段中, name
屬性通過后端,但由於某種原因user
屬性沒有。 我嘗試使用console.log
來查看user
在發送到后端時是否未定義,但不是。
我試圖改變我在發送到后端的 object 中定義鍵的方式,這樣它們就不像這個答案那樣是動態的,結果也沒有改變。
user
變量是一個包含 email 地址的字符串,例如'foobar@gmail.com'
。 我想也許 Django 出於安全原因默認阻止它,但我在@
處拆分字符串並嘗試僅發送第一部分,但它也不起作用。
我還嘗試定義一個新變量,以便user
的類型是string
而不是string | undefined
string | undefined
,它仍然無法正常工作。 title
的值也是字符串,它通過后端(它在我的序列化程序在其create()
方法中看到的validated_data
數據 object 中定義)。 所以我不知道這可能是什么。 以下是我的一些文件:
axiosAPI.ts
import axios from 'axios'
const baseURL = 'http://localhost:8000/'
export const axiosInstance = axios.create({
baseURL: baseURL,
timeout: 5000,
headers: {
'xsrfHeaderName': 'X-CSRFTOKEN',
'xrsfCookieName': 'csrftoken',
'Authorization': localStorage.getItem('access_token') ? 'JWT ' + localStorage.getItem('access_token') : null,
'Content-Type': 'application/json',
'accept': 'application/json',
}
})
axiosInstance.interceptors.response.use(
response => response,
error => {
const originalRequest = error.config
// Prevent infinite loops if login credentials invalid
if (error.response.status === 406 && originalRequest.url === baseURL + 'auth/token/obtain') {
window.location.href = '/login/'
return Promise.reject(error);
}
// Respond to expired refresh tokens
if (error.response.data.code === 'token_not_valid' && error.response.status === 401 && error.response.statusText === 'Unauthorized') {
const refreshToken = localStorage.getItem('refresh_token')
if (refreshToken) {
const tokenObj = JSON.parse(atob(refreshToken.split('.')[1]))
const currentTime = Math.ceil(Date.now() / 1000)
if (tokenObj.exp > currentTime) {
return axiosInstance
.post('/auth/token/refresh/', { refresh: refreshToken })
.then(response => {
localStorage.setItem('access_token', response.data.access)
localStorage.setItem('refresh_token', response.data.refresh)
axiosInstance.defaults.headers['Authorization'] = 'JWT ' + response.data.access
originalRequest.headers['Authorization'] = 'JWT ' + response.data.access
return axiosInstance(originalRequest);
})
.catch(err => {
console.log(err)
throw err;
})
} else {
console.log('Refresh token is expired.')
window.location.href = '/login/'
}
} else {
console.log('Refresh token not available.')
window.location.href = '/login/'
}
}
// Respond to invalid access tokens
if (error.response.status === 401 && error.response.statusText === 'Unauthorized') {
const refreshToken = localStorage.getItem('refresh_token')
return axiosInstance
.post('/auth/token/refresh/', { refresh: refreshToken })
.then(response => {
localStorage.setItem('access_token', response.data.access)
localStorage.setItem('refresh_token', response.data.refresh)
axiosInstance.defaults.headers['Authorization'] = 'JWT ' + response.data.access
originalRequest.headers['Authorization'] = 'JWT ' + response.data.access
return axiosInstance(originalRequest)
}).catch(err => {
console.log(err)
throw err;
})
}
}
)
notebooks/serializers.py
import bleach
import json
from rest_framework import serializers
from authentication.models import ErsatzNoteUser
from .models import Note, Notebook
class NoteSerializer(serializers.ModelSerializer):
note_id = serializers.SlugField(source='id', read_only=True, required=False)
title = serializers.JSONField(required=False)
content = serializers.CharField(required=False)
notebook = serializers.PrimaryKeyRelatedField(read_only=True, required=False)
date_modified = serializers.DateField(read_only=True, required=False)
date_created = serializers.DateField(read_only=True, required=False)
def create(self, validated_data):
title = json.dumps(validated_data['title'])
# Workaround to fix a currently unpatched bug in Slate
# that occurs when an editor's contents begin with a list
content = validated_data['content']
if content.startswith('<ul') or content.startswith('<ol'):
content = '<p></p>' + content
response_data = {
'title': title,
'content': content,
}
return Note.objects.create(**response_data)
def update(self, instance, validated_data):
instance.title = json.dumps(validated_data['title'])
# See the above comment in the 'create' method
content = validated_data['content']
if content.startswith('<ul') or content.startswith('<ol'):
content = '<p></p>' + content
instance.content = content
instance.save()
return instance
class Meta:
model = Note
fields = [ 'note_id', 'title', 'content', 'notebook', 'date_modified', 'date_created' ]
class NotebookSerializer(serializers.ModelSerializer):
notebook_id = serializers.SlugField(source='id', read_only=True, required=False)
name = serializers.CharField(max_length=64, default='')
notes = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
def create(self, validated_data):
# notebook_data = {
# 'name': validated_data['name'],
# 'user': ErsatzNoteUser.objects.get(email=validated_data['user']),
# }
print(validated_data)
return Notebook.objects.create(name=validated_data['name'], user=ErsatzNoteUser.objects.get(email=validated_data['user']))
class Meta:
model = Notebook
fields = [ 'notebook_id', 'name', 'notes', 'date_modified', 'date_created' ]
notebooks/views.py
import json
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from .serializers import NoteSerializer, NotebookSerializer
from .models import Note, Notebook
class NoteViewSet(viewsets.ModelViewSet):
serializer_class = NoteSerializer
def get_queryset(self):
return self.request.user.notes.all()
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = NoteSerializer(instance)
title = json.loads(serializer.data['title'])
response_data = {
'note_id': serializer.data['note_id'],
'title': title,
'content': serializer.data['content'],
'notebook': serializer.data['notebook'],
'date_modified': serializer.data['date_modified'],
'date_created': serializer.data['date_created'],
}
return Response(response_data)
class NotebookViewSet(viewsets.ModelViewSet):
serializer_class = NotebookSerializer
def get_queryset(self):
return self.request.user.notebooks.all()
notebooks/models.py
from django.db import models
from django.contrib.auth.models import User
from jsonfield import JSONField
from django.conf import settings
from .helpers import generate_slug
class Note(models.Model):
""" Represents an individual note. """
id = models.SlugField(max_length=settings.MAX_SLUG_LENGTH, primary_key=True)
title = JSONField(null=True)
content = models.TextField(null=True)
notebook = models.ForeignKey('Notebook', related_name='notes', on_delete=models.CASCADE, null=True, blank=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='notes', on_delete=models.CASCADE, null=True, blank=True)
date_created = models.DateField(auto_now_add=True)
date_modified = models.DateField(auto_now=True)
def save(self, *args, **kwargs):
if not self.id:
self.id = generate_slug(self, settings.MAX_SLUG_LENGTH)
# Temporary expedients for the sake of development
if not self.notebook:
self.notebook = Notebook.objects.get(id='YyOzNhMFMPtN8HM')
super(Note, self).save(*args, **kwargs)
def __str__(self):
return self.id
class Meta:
ordering = [ '-date_modified', '-date_created', ]
class Notebook(models.Model):
""" A collection of individual notes. """
id = models.SlugField(max_length=settings.MAX_SLUG_LENGTH, primary_key=True)
name = models.CharField(max_length=64, default='')
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='notebooks', on_delete=models.CASCADE)
date_created = models.DateField(auto_now_add=True)
date_modified = models.DateField(auto_now=True)
def save(self, *args, **kwargs):
if not self.id:
self.id = generate_slug(self, settings.MAX_SLUG_LENGTH)
super(Notebook, self).save(*args, **kwargs)
def __str__(self):
return self.name
class Meta:
ordering = [ '-date_modified', '-date_created', 'name' ]
請不要猶豫,詢問您是否需要更多信息。
編輯:更改NotebookViewSet
使其具有retrieve()
方法,結果沒有改變:
class NotebookViewSet(viewsets.ModelViewSet):
serializer_class = NotebookSerializer
def get_queryset(self):
return self.request.user.notebooks.all()
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = NotebookSerializer(instance)
response_data = {
'name': serializer.data['name'],
'user': serializer.data['user'],
}
return Response(response_data)
在您的示例中,您的NotebookViewSet
class 沒有retrieve
方法。
事實證明,在幕后,DRF 認為ForeignKey
字段是read_only
。 這意味着它僅用於表示 object,不應成為任何創建或更新過程的一部分。 根據文檔:
只讀字段包含在 API output 中,但在創建或更新操作期間不應包含在輸入中。 任何錯誤地包含在序列化程序輸入中的“read_only”字段都將被忽略。
但這對我的數據庫無關緊要,這要求我在創建每個新Notebook
時識別與它對應的用戶,否則違反了 NOT NULL 約束。 所以我只是重寫了默認的ModelViewSet
create()
方法,將user
字符串手動傳遞給我的NotebookSerializer
作為context
的一部分,允許我使用它來獲取數據庫中相應的用戶 object 並將其分配給新創建的Notebook
。 我是這樣處理的:
class NotebookViewSet(viewsets.ModelViewSet):
serializer_class = NotebookSerializer
def get_queryset(self):
return self.request.user.notebooks.all()
def perform_create(self, serializer):
serializer.save()
def create(self, request, *args, **kwargs):
serializer = NotebookSerializer(data=request.data, context={ 'request': request })
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
class NotebookSerializer(serializers.ModelSerializer):
notebook_id = serializers.SlugField(source='id', read_only=True, required=False)
name = serializers.CharField(max_length=64, default='')
notes = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
def create(self, validated_data):
return Notebook.objects.create(
user=ErsatzNoteUser.objects.get(email=self.context['request'].user),
**validated_data
)
class Meta:
model = Notebook
fields = [ 'notebook_id', 'name', 'notes', 'date_modified', 'date_created' ]
這就是全部。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.