React + Djangoでフロント画面からの送信データをバックエンドでCRUDする機能実装

実現したいこと

  • バックエンド側で設計したAPIエンドポイントにアクセスしCRUD処理を実装したい
    • フロント: React / Redux
    • バックエンド: Python / Django / Django REST Framework
  • 現状発生している問題の原因と必要な設計を明確にしたい
  • APIツールPOSTMANを使う場合にCSRFトークンが必要か、設定方法を知りたい

発生している問題・分からないこと

フロントでの画面参照時にデータ取得ができているものの、フォームを編集して更新処理(ボタン押下)時にアクセスできない状態になっている。
HttpResponseの問題とは出ているが、具体的な解消法が見つからないです。
POSTMANを使った場合も同様のエラーでした。

エラーメッセージ

error

1raise ValueError( 2ValueError: The view django_app.views.mypage_edit_profile didn't return an HttpResponse object. It returned None instead. 3[06/Feb/2024 15:23:15] "PATCH /api/mypage/edit_profile/1 HTTP/1.1" 500 64127

該当のソースコード

python:

1from django.urls import path, include 2from . import views 3 4urlpatterns = [5 :6 path("mypage/user_profile/", views.mypage_user_profile, name="mypage_user_profile"),7 path("mypage/user_profile/<int:pk>", views.mypage_user_profile, name="mypage_user_profile"),8 path("mypage/edit_profile/<int:pk>", views.mypage_edit_profile, name="mypage_edit_profile"), 9]

python:

1from django.shortcuts import render 2from django.http import JsonResponse 3from rest_framework import viewsets 4 5from django.views.decorators.csrf import csrf_exempt 6 :7# マイページ: ユーザープロフィールを取得するAPI8def mypage_user_profile(request, pk=None):9 10 if pk is None:11 queryset = MypageUserProfile.objects.all()12 else:13 queryset = MypageUserProfile.objects.filter(id=pk)14 serializer_class = MypageUserProfileSerializer(queryset, many=True)15 data = serializer_class.data 16 17 return JsonResponse(data, safe=False)18 19# マイページ: ユーザープロフィールを更新するAPI20@csrf_exempt21def mypage_edit_profile(request, pk=None):22 23 queryset = MypageUserProfile.objects.filter(id=pk)24 print("queryset: ", queryset)25 print("request.POST: ", request.POST)26 :27 # serializer_class = MypageUserProfileSerializer(queryset, many=True)28 # data = serializer_class.data29 30 # return JsonResponse(data, safe=False)31

python:

1from django.db import models 2from django.utils import timezone 3 : 4# マイページ: ユーザープロフィールを取得するAPI5class MypageUserProfile(BaseMeta):6 id = models.AutoField(primary_key=True)7 name = models.CharField(max_length=255)8 account_id = models.CharField(max_length=255)9 password = models.CharField(max_length=255)10 email = models.CharField(max_length=255)11 zip = models.CharField(max_length=7)12 address = models.CharField(max_length=255)13 phone = models.CharField(max_length=11, null=True)14 member_type = models.ForeignKey(PricingPlan, on_delete=models.PROTECT, null=True)15 16 class Meta:17 db_table = 'mypage_user_profile'18 verbose_name_plural = 'マイページ_ユーザープロフィール'19 20 def __str__(self):21 return self.name

javascript:

1import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";2import axios from "axios";3 4const initialState = {5 isLoading: false,6 items: [],7};8 :9export const fetchGetMypageProfile = createAsyncThunk(10 "get_mypage_profile", 11 async (id) => {12 const response = await axios.get(`${BASE_API_URL}/mypage/user_profile/${id}`);13 return response.data[0];14 }15);16 17// 更新18export const fetchUpdateMypageProfile = createAsyncThunk(19 "update_mypage_profile",20 async (data) => {21 try {22 const response = await axios.post(`${BASE_API_URL}/mypage/edit_profile/${data.id}`, data);23 console.log("updateMypageProfile: ", response);24 return response.data;25 }26 catch (error) {27 console.log("updateMypageProfile_error: ", error);28 }29 }30);31 32// createSliceは省略

javascript:

1import React, { useState, useEffect } from 'react'2import Axios from 'axios'3 4import { Link } from 'react-router-dom'5import { useSelector, useDispatch } from 'react-redux'6 7import store from '../../store'8 9import { fetchGetMypageProfile, fetchUpdateMypageProfile } from '../../features/mypage/mypageProfileSlice'10 11const EditProfile = (props) => {12 const user_id = props.user_id;13 console.log("store: ", store.getState());14 15 const currentUserList = useSelector((state) => state.mypageProfileReducer.items);16 const isLoading = useSelector((state) => state.mypageProfileReducer.isLoading);17 const dispatch = useDispatch();18 19 const [userList, setUserList] = useState(currentUserList);20 21 // 初回のみ実行22 useEffect(() => {23 setUserList(currentUserList);24 }, [currentUserList]);25 26 useEffect(() => {27 dispatch(fetchGetMypageProfile(user_id));28 }, [dispatch, user_id]);29 30 const handleSubmit = (e, newUserList) => {31 e.preventDefault();32 :33 dispatch(fetchUpdateMypageProfile(newUserList));34 }35 36 return (37 :38 <form onSubmit={e => {handleSubmit(e, userList);}}>39 <TableContainer component={Paper}>40 <Table sx={{ minWidth: 650 }} aria-label="simple table">41 <TableBody>42 <TableRow >43 <TableCell component="th" scope="row">44 ID45 </TableCell>46 <TableCell align="right">{userList.id}</TableCell>47 </TableRow>48 <TableRow >49 <TableCell component="th" scope="row">50 氏名 51 </TableCell>52 <TableCell align="right">53 <TextField required id="outlined-basic" label="Required" variant="outlined"54 value={userList.name}55 onChange={e => setUserList({...userList, name: e.target.value})}56 />57 </TableCell>58 </TableRow>59 <TableRow >60 <TableCell component="th" scope="row">61 アカウントID62 </TableCell>63 <TableCell align="right">64 <TextField required id="outlined-basic" label="Required" variant="outlined"65 value={userList.account_id}66 onChange={e => setUserList({...userList, account_id: e.target.value})}67 />68 </TableCell>69 </TableRow>70 <TableRow >71 <TableCell component="th" scope="row">72 パスワード 73 </TableCell>74 <TableCell align="right">**********</TableCell>75 </TableRow>76 <TableRow >77 <TableCell component="th" scope="row">78 メールアドレス 79 </TableCell>80 <TableCell align="right">81 <TextField required id="outlined-basic" label="Required" variant="outlined"82 value={userList.email}83 onChange={e => setUserList({...userList, email: e.target.value})}84 />85 </TableCell>86 </TableRow>87 <TableRow >88 <TableCell component="th" scope="row">89 郵便番号 90 </TableCell>91 <TableCell align="right">92 <TextField required id="outlined-basic" label="Required" variant="outlined"93 value={userList.zip}94 onChange={e => setUserList({...userList, zip: e.target.value})}95 />96 </TableCell>97 </TableRow>98 <TableRow >99 <TableCell component="th" scope="row">100 住所 101 </TableCell>102 <TableCell align="right">103 <TextField required fullWidth id="outlined-basic" label="Required" variant="outlined"104 value={userList.address}105 onChange={e => setUserList({...userList, address: e.target.value})}106 />107 </TableCell>108 </TableRow>109 <TableRow >110 <TableCell component="th" scope="row">111 電話番号 112 </TableCell>113 <TableCell align="right">114 <TextField required id="outlined-basic" label="Required" variant="outlined"115 value={userList.phone}116 onChange={e => setUserList({...userList, phone: e.target.value})}117 />118 </TableCell>119 </TableRow>120 </TableBody>121 </Table>122 </TableContainer>123 124 <Box className='section-footer'>125 <Button variant="contained" color="primary" type='submit'>126 プロフィールを更新する 127 </Button>128 </Box>129 </form>130 )131}132 133export default EditProfile134

試したこと・調べたこと

上記の詳細・結果

HTTPリクエストなどバックエンド側に問題がある前提で調べましたが、的確な解決例が見つかりませんでした。
元々403エラーが出ていたため、解決策を調べ仮実装としてCSRFを回避する csrf_exempt を追記しましたが解消できていない状態です。

補足

特になし

コメントを投稿

0 コメント