簡體   English   中英

Django/DRF 后端未從 POST 請求接收完整數據

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM