Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

【Django Rest Framework】新規登録機能API実装で400エラー

解決したいこと

→フロントエンド:Vue.js
→バックエンド:Django Rest Framework

400エラーがでてしまったので解決したいです

エラー内容

POST http://localhost:8000/api/resister/ 400 (Bad Request)
dispatchXhrRequest @ xhr.js:251
xhr @ xhr.js:49
dispatchRequest @ dispatchRequest.js:51
request @ Axios.js:148
httpMethod @ Axios.js:187
wrap @ bind.js:5
resister @ MemberForm.vue:133
callWithErrorHandling @ runtime-core.esm-bundler.js:173
callWithAsyncErrorHandling @ runtime-core.esm-bundler.js:182
emit @ runtime-core.esm-bundler.js:730
eval @ runtime-core.esm-bundler.js:7465
handleClick @ use-button.ts:57
callWithErrorHandling @ runtime-core.esm-bundler.js:173
callWithAsyncErrorHandling @ runtime-core.esm-bundler.js:182
invoker @ runtime-dom.esm-bundler.js:345
MemberForm.vue:145 APIリクエストが失敗しました 

エラー画面
スクリーンショット 2023-08-25 093252.png

エラー内容

ソースコード
<template>
  <div class="member-form-container">
    <h2>新規登録</h2>
    <img
      src="@/assets/スタート.jpg"
      alt="SAY HELLO TO BIKE ロゴ"
      style="width: 30%"
    />
    <ul id="example-1">
      <li v-for="message in validationErrors.validates" :key="message">
        {{ message }}
      </li>
    </ul>
    <!-- <el-form :model="form" class="custom-form" style="width: 40%"> -->
    <el-form class="custom-form" style="width: 30%">
      <el-form-item label="ニックネーム" class="custom-form-item">
        <el-input
          v-model="init.nickname"
          placeholder="ニックネームを入力してください"
          class="custom-input"
        />
      </el-form-item>
      <el-form-item label="メールアドレス" class="custom-form-item">
        <el-input
          v-model="init.email"
          placeholder="メールアドレスを入力してください"
          class="custom-input"
        />
      </el-form-item>
      <el-form-item label="パスワード" class="custom-form-item">
        <el-input
          v-model="init.password"
          placeholder="パスワードを入力してください"
          class="custom-input"
        />
      </el-form-item>
      <el-form-item label="パスワード(確認)" class="custom-form-item">
        <el-input
          v-model="init.passwordconfirm"
          placeholder="パスワードを入力してください"
          class="custom-input"
        />
      </el-form-item>
      <p>好きな観光地について書いてください</p>
      <el-form-item class="custom-form-item">
        <el-input
          v-model="init.favorite"
          placeholder="好きな観光地について書いてください"
          :autosize="{ minRows: 2, maxRows: 5 }"
          class="custom-input"
          type="textarea"
        />
      </el-form-item>
      <el-form-item class="custom-form-item">
        <el-date-picker
          v-model="init.birth_day"
          type="date"
          placeholder="誕生日を選択してください"
          class="custom-picker"
          value-format="YYYY-MM-DD"
        />
      </el-form-item>
      <div class="fileInput-container">
        <input
          type="file"
          @change="handleFileUpload($event)"
          accept="image/*"
          capture
        />
      </div>
    </el-form>
    <div class="button-container">
      <el-button @click="resister" type="primary">登録</el-button>
      <el-button @click="logout" type="primary">TOP</el-button>
    </div>
  </div>
</template>


<script setup>
import { useRouter } from "vue-router";
import { reactive, computed, ref } from "vue";
import "element-plus/dist/index.css";
import validations from "@/module/validate";
import axios from "axios";

// Axiosの設定
axios.defaults.withCredentials = true;
const file = ref(null);
const router = useRouter();
const init = reactive({
  nickname: "",
  email: "",
  password: "",
  passwordconfirm: "",
  favorite: "",
  birth_day: "",
});

const validationErrors = computed(() => {
  const errors = {
    validates: [],
  };
  validations.emailValidate(init.email, errors.validates);
  validations.passwordValidate(init.password, errors.validates);

  return errors;
});
// ファイルをアップロードする準備をする

const handleFileUpload = ($event) => {
  file.value = $event.target.files[0];
};

const resister = () => {
  // フォームのデータをAPIに送信する処理
  const data = {
    user: {
      username: init.nickname,
      email: init.email,
      password: init.password,
    },
    // email: init.email,
    // password: init.password,
    favorite: init.favorite,
    birth_day: init.birth_day,
    image: file.value,
  };
  console.log("request",data);
  // APIエンドポイントに対してPOSTリクエストを送信
  // @TODO API実装後に保存
  axios
    .post("http://localhost:8000/api/resister/", data, {
      headers: {
        "Content-Type": "multipart/form-data", // ファイルを含むフォームデータの場合は必要
      },
    })
    .then((response) => {
      // 成功時の処理
      console.log("APIリクエストが成功しました", response.data);
    })
    .catch((error) => {
      // エラー時の処理
      console.error("APIリクエストが失敗しました", error);
    });
view.py
from rest_framework import viewsets, status
from rest_framework.response import Response
from rest_framework.parsers import MultiPartParser
from .models import UserInfo
from .serializer import UserInfoSerializer, UserSerializer
# ロガーの設定
import logging
# ロガーの設定
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)  # デバッグレベルのログを出力
class UserInfoViewSet(viewsets.ModelViewSet):
    queryset = UserInfo.objects.all()
    serializer_class = UserInfoSerializer
    parser_classes = [MultiPartParser]  # ファイルアップロード用のパーサーを追加
    
def create(self, request, *args, **kwargs):
    user_data = request.data.get('user')
    user_serializer = UserSerializer(data=user_data)
    if user_serializer.is_valid():
        user = user_serializer.save()
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            serializer.save(user=user)
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    else:
        return Response(user_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
selializer.py
from rest_framework import serializers
from django.contrib.auth.models import User
from .models import UserInfo

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'username', 'email', 'password']
        extra_kwargs = {'password': {'write_only': True}}
class UserInfoSerializer(serializers.ModelSerializer):
    user = UserSerializer()  # Userモデルのシリアライザーをネスト
    class Meta:
        model = UserInfo
        # json で出力するフィールド
        fields = ('id','user', 'birth_day','favorite','created_at' ,'image')
    def update(self, instance, validated_data):
    # userフィールドのデータを取得
        user_data = validated_data.pop('user', None)
        if user_data:
            # userフィールドの更新処理を実装
            instance.user.username = user_data.get('username', instance.user.username)
            instance.user.email = user_data.get('email', instance.user.email)
            instance.user.save()
        # インスタンスを保存して更新したデータを返す
        instance.save()
        return instance
    
    def delete(self, instance):
        instance.delete()

確認したこと

1.headerがあっているか
"Content-Type": "multipart/form-data",
2.リクエストパラメータは合致しているか
3.view.pyのcreateメソッドは正常によばれているか

等を確認しましたが400エラーが解消されません。

さいごに

今回画像とテキストを混ぜてリクエストを送っているため、その辺が不安です
お忙しいところ恐縮ですが宜しくお願い致します。

0

1Answer

実際にコード確認はできておりませんので、所見に留まりますが
postデータが

  const data = {
     ...
  }
  axios
    .post("http://localhost:8000/api/resister/", data, {

とjavascriptのObjectになっておりますが。
axiosのFormDataを使用し

  const formData = new FormData();
  let user = JSON.stringfy(data.user); //ネストしているデータはjsonに直す
  formData.append("user",user);
  formData.append("favorite", init.favorite);
  ...
  axios
    .post("http://localhost:8000/api/resister/", formData, {

とFormDataを使用する形ではなかったでしょうか?
(version違いでjavascript objectをそのまま使用できるようになっていたら申し訳ございません。)

ご参考:https://axios-http.com/docs/multipart

1Like

Comments

  1. @gucho-n

    Questioner

    ありがとうございます。
    また返信が遅くなり申し訳ありません。
    まずjson形式に忘れていたのが1点
    そしてformdataが必須かどうかわからずやってしまったのが1点でした、
    うまくいったらまたご報告させてください!

  2. @gucho-n

    Questioner

    遅くなり申し訳ございません。でした
    以上のようにformdataをうまく使ったら実装できました!
    ありがとうございました。

Your answer might help someone💌