【Next.js×Django】ユーザー登録画面を作る

Next.jsとDjagoでのユーザ登録画面を作成する方法を備忘録として記載します。

※`Image`の箇所はいったんダミーで入れています。

# フロント側(Nexct.js)の実装

:::note warn
`FormData`形式でPOST送信するときは、パラメータ名が`Django`のSerializerクラスで定義されているパラメータと一致するように記載しましょう。
#### (例)
|Next.js側のFormData|DjangoのSerilizer|
|-------------------|-----------------|
|generalUserId      |generalUserId    |
|generalUserName    |generalUserName  |

フロント側とサーバ側のパラメータ名を一致させないと`400 Bad Request`が出てしまい失敗します。

:::

```frontend/app/generalUsers/registration/page.tsx
'use client'
import { useState } from "react"
import { useRouter } from "next/navigation"
import { useForm } from "react-hook-form"
import axios from "axios"
import { error } from "console"
import { CldImage } from "next-cloudinary"
import { CldUploadWidget } from "next-cloudinary"

export default function Registration(){
    const router = useRouter();

    const defaultValues = {
        Id:'',
        Name:'',
        Email:'',
        Password:'',
        Department:'',
        DepartmentPhone:''
    }

    const {register,handleSubmit,formState:{errors}} = useForm({
        defaultValues
    })
    const [id,setId] = useState('');
    const [name,setName] = useState('');
    const [image,setImage] = useState<File | null>(null);
    const [email,setEmail] = useState('');
    const [password,setPassword] = useState('');
    const [department,setDepartment] = useState('');
    const [departmentPhone,setDeepartmentPhone] = useState('');

    
    const onSubmit = async(data:any)=>{
        try {
            const formData = new FormData();
            formData.append('generalUserId',id);
            formData.append('generalUserName',name);
            setImage(null);
            formData.append('generalUserImage',"aa");
            formData.append('generalUserEmail', email);
            formData.append('generalUserPassword', password);
            formData.append('generalUserDepartment', department);
            formData.append('generalUserDepartmentPhone', departmentPhone);

            const response = await axios.post('http://127.0.0.1:8000/generalusers/registergeneralusers/', formData);

            if (response.status !== 201) {
                alert('User has not been registered.');
                return;
            }

            alert('Successfully registered user');
            console.log(response.data);
        } catch (error) {
            console.log('Error uploading image or submitting user data:', error);
            alert('Error uploading image or submitting user data.');
        }
    };
    
    return (
        <div>
            <form onSubmit={handleSubmit(onSubmit)}>
                <div>
                    <div>
                        <label>ID</label>
                        <input
                            type="text"
                            className="border border-gray-500 rounded-md px-4 py-4"
                            defaultValue={defaultValues.Id}
                            {...register('Id',{
                                required:'ID must be required'
                            })}
                            onChange={(e)=>setId(e.target.value)}
                        />
                        <div className="text-rose-500">{errors.Id?.message}</div>
                    </div>
                    <div>
                        <label>Name</label>
                        <input
                            type="text"
                            className="border border-gray-500 rounded-md px-4 py-4"
                            defaultValue={defaultValues.Name}
                            {...register('Name',{
                                required:'Name must be required.'
                            })}
                            onChange={(e)=>setName(e.target.value)}
                        />
                        <div className="text-rose-500">{errors.Name?.message}</div>
                    </div>
                    <div>
                        <label>Email</label>
                        <input
                            type="email"
                            className="border border-gray-500 rounded-md px-4 py-4"
                            defaultValue={defaultValues.Email}
                            {...register('Email',{
                                required:'Mail address must be required.'
                            })}
                            onChange={(e)=>setEmail(e.target.value)}
                        />
                        <div className="text-rose-500">{errors.Email?.message}</div>
                    </div>
                    <div>
                        <label>Password</label>
                        <input
                            type="password"
                            className="border border-gray-500 rounded-md px-4 py-4"
                            defaultValue={defaultValues.Password}
                            {...register('Password',{
                                required:'Password must be required.'
                            })}
                            onChange={(e)=>setPassword(e.target.value)}
                        />
                        <div className="text-rose-500">{errors.Password?.message}</div>
                    </div>
                    <div>
                        <label>Department</label>
                        <input
                            type="text"
                            className="border border-gray-500 rounded-md px-4 py-4"
                            defaultValue={defaultValues.Department}
                            {...register('Department',{
                                required:'Department must be required.'
                            })}
                            onChange={(e)=>setDepartment(e.target.value)}
                        />
                        <div className="text-rose-500">{errors.Department?.message}</div>
                    </div>
                    <div>
                        <label>Department Phone<small>Direct Phone</small></label>
                        <input
                            type="text"
                            className="border border-gray-500 rounded-md px-4 py-4"
                            defaultValue={defaultValues.DepartmentPhone}
                            {...register('DepartmentPhone',{
                                required:'DepartmentPhone must be required.'
                            })}
                            onChange={(e)=>setDeepartmentPhone(e.target.value)}
                        />
                        <div className="text-rose-500">{errors.DepartmentPhone?.message}</div>
                    </div>
                    <button
                        type="submit"
                        className="bg bg-blue-500 rounded-md text-white px-4 py-4"
                    >
                        Register
                    </button>
                    <button
                        type="button"
                        className="bg bg-green-500 rounded-md text-white px-4 py-4"
                    >
                        BackToTopPage
                    </button>
                </div>
            </form>
        </div>
    )
}


```

# サーバサイド(Django)側の実装

```backend/generalusers/models.py
from django.db import models

# Create your models here.
class GeneralUsers(models.Model):
    generalUserId = models.CharField(blank=False,null=False)
    generalUserName = models.CharField(blank=False,null=False)
    generalUserImage = models.CharField(blank=True,null=True)
    generalUserEmail = models.CharField(blank=False,null=False)
    generalUserPassword = models.CharField(blank=False,null=False)
    generalUserDepartment = models.CharField(blank=False,null=False)
    generalUserDepartmentPhone = models.CharField(blank=False,null=False)

    def __str__(self):
        return self.generalUserId

```

```backend/generalusers/models.py
from rest_framework import serializers
from .models import GeneralUsers
from django.contrib.auth.hashers import make_password

class GeneralUsersSerializer(serializers.ModelSerializer):
    class Meta:
        model = GeneralUsers
        fields = ['generalUserId','generalUserName','generalUserImage','generalUserEmail','generalUserPassword','generalUserDepartment','generalUserDepartmentPhone']
    
    #generalUserImage = serializers.ImageField(required=False, allow_null=True)
                                              
    def create(self,validated_data):
        password = validated_data['generalUserPassword']
        hashed_password = make_password(password)
        validated_data['generalUserPassword'] = hashed_password

        return super().create(validated_data)
```

```backend/generalusers/urls.py
from django.urls import path,include
from .views import GeneralUsersViewSet #Add
from rest_framework.routers import DefaultRouter
#from .views import GeneralUsersViewSet

router = DefaultRouter()
router.register('registergeneralusers',GeneralUsersViewSet,basename='create_general_user')

urlpatterns =[
    path('',include(router.urls))
]
```

```backend/generalusers/viewss.py
#from django.shortcuts import render
from .models import GeneralUsers
from .serializers import GeneralUsersSerializer
from rest_framework import viewsets
from rest_framework.decorators import api_view #追加
from rest_framework.response import Response # 追加
from rest_framework import status #追加
from django.db import transaction # 追加
from django.contrib.auth.hashers import make_password #追加
from django.core.exceptions import ObjectDoesNotExist #Add
from django.contrib.auth.hashers import check_password #Add
from .serializers import GeneralUsersSerializer
# Create your views here.

class GeneralUsersViewSet(viewsets.ModelViewSet):
    queryset = GeneralUsers.objects.all()
    serializer_class = GeneralUsersSerializer

@api_view(['POST'])
def create_general_user(request):
    if request.method == 'POST':
        with transaction.atomic():
            serializer = GeneralUsersSerializer(data=request.data)
            if serializer.is_valid():
                password = serializer.validated_data['password']
                hashed_password = make_password(password)

                serializer.validated_data['generalUserPassword'] = hashed_password
                if serializer.is_valid():
                    serializer.save()
                    return Response(serializer.data,status=status.HTTP_201_CREATED)
                else:
                    return Response(serializer.errors,status=status.HTTP_500_INTERNAL_SERVER_ERROR)
```

```backend/generalusers/vserializer.py
from rest_framework import serializers
from .models import GeneralUsers
from django.contrib.auth.hashers import make_password

class GeneralUsersSerializer(serializers.ModelSerializer):
    class Meta:
        model = GeneralUsers
        fields = ['generalUserId','generalUserName','generalUserImage','generalUserEmail','generalUserPassword','generalUserDepartment','generalUserDepartmentPhone']
    
    #generalUserImage = serializers.ImageField(required=False, allow_null=True)
                                              
    def create(self,validated_data):
        password = validated_data['generalUserPassword']
        hashed_password = make_password(password)
        validated_data['generalUserPassword'] = hashed_password

        return super().create(validated_data)
```