1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Next.js×Django連携】管理者画面を作成する方法

Posted at

サンプル

管理者画面から登録に必要な情報を入力して登録ボタンをクリックします。
image.png

正常に登録できたら下記のようにデータベースに登録されます。
image.png

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

fronend/app/adminUsers/registration/page.tsx
'use client'
import { useState,useEffect } from "react"
import { useRouter } from "next/navigation"
import axios from "axios"
import { useForm } from "react-hook-form"


export default function AdminUserRegistration(){
    const router = useRouter();
    const defaultValues = {
        adminUserName:'',
        adminUserEmail:'',
        adminUserPhone:'',
        adminUserPassword:'',
        adminUserCompany:'',
        adminUserDepartment:''
    };

    const {register,handleSubmit,formState:{errors}} = useForm({
        defaultValues
    });
    const [adminUserName,setAdminUserName] = useState('');
    const [adminUserEmail,setAdminUserEmail] = useState('');
    const [adminUserPhone,setAdminUserPhone] = useState('');
    const [adminUserPassword,setAdminUserPassword] = useState('');
    const [adminUserCompany,setAdminUserCompany] = useState('');
    const [adminUserDepartment,setAdminUserDepartment] = useState('');

    const onSubmit = async (data: any) => {
        const formData = new FormData();
        formData.append('adminUserName',adminUserName);
        formData.append('adminUserEmail',adminUserEmail);
        formData.append('adminUserPhone',adminUserPhone);
        formData.append('adminUserPassword',adminUserPassword);
        formData.append('adminUserCompany',adminUserCompany);
        formData.append('adminUserDepartment',adminUserDepartment);
        try {
            // 正常なレスポンスを処理
            const response = await axios.post('http://127.0.0.1:8000/api/adminusers/', formData);
            if (response.status === 201) {
                alert('管理者を登録できたぜ');
                console.log('編きゃされたレスポンスは、',response);
                router.push('/');
            } else {
                console.log('Unexpected status code:', response.status);
            }
        } catch (error) {
            // エラーを処理
            console.error(error);
            alert('Error occurred while registering the admin user');
        }
    };

    return (
        <form onSubmit={handleSubmit(onSubmit)}>
            <div>
                <label>Name</label>
                <input
                    type="text"
                    className="w-full border border-gray-500 rounded-md"
                    defaultValue={defaultValues.adminUserName}
                    {...register('adminUserName',{
                        required:'AdminUserNamwe must be required.'
                    })}
                    onChange={(e)=>setAdminUserName(e.target.value)}
                />
                <div className="text-rose-500">{errors.adminUserName?.message}</div>
            </div>
            <div>
                <label>Email</label>
                <input
                    type="email"
                    className="w-full border border-gray-500 rounded-md"
                    defaultValue={defaultValues.adminUserEmail}
                    {...register('adminUserEmail',{
                        required:'AdminuserEmail must be required.'
                    })}
                    onChange={(e)=>setAdminUserEmail(e.target.value)}
                />
                <div className="text-rose-500">{errors.adminUserEmail?.message}</div>
            </div>
            <div>
                <label>Phone</label>
                <input
                    type="text"
                    className="w-full border border-gray-500 rounded-md"
                    defaultValue={defaultValues.adminUserPhone}
                    {...register('adminUserPhone',{
                        required:'AdminUserPhone must be required.'
                    })}
                    onChange={(e)=>{setAdminUserPhone(e.target.value)}}
                />
                <div className="text-rose-500">{errors.adminUserPhone?.message}</div>
            </div>
            <div>
                <label>Password</label>
                <input
                    type="password"
                    className="w-full border border-gray-500 rounded-md"
                    defaultValue={defaultValues.adminUserPassword}
                    {...register('adminUserPassword',{
                        required:'AdminUserPassword must be required.'
                    })}
                    onChange={(e)=>setAdminUserPassword(e.target.value)}
                />
                <div className="text-rose-500">{errors.adminUserPassword ?.message}</div>
            </div>
            <label>Company</label>
            <input
                type="text"
                className="w-full border border-gray-500 rounded-md"
                defaultValue={defaultValues.adminUserCompany}
                {...register('adminUserCompany',{
                    required:'Company must be required.'
                })}
                onChange={(e)=>setAdminUserCompany(e.target.value)}
            />
            <div className="text-rose-500">{errors.adminUserCompany?.message}</div>
            <div>
                <label>Department</label>
                <input
                    type="text"
                    className="w-full border border-gray-500 rounded-md"
                    defaultValue={defaultValues.adminUserDepartment}
                    {...register('adminUserDepartment',{
                        required:'Departmanet muset be required.'
                    })}
                    onChange={(e)=>{setAdminUserDepartment(e.target.value)}}
                />
                <div className="text-rose-500">{errors.adminUserDepartment?.message}</div>
            </div>
            <div>
                <button
                    type="submit"
                    className="bg bg-blue-500 text-white rounded-md px-4 py-4"
                >
                    Register
                </button>
                <button
                    type="button"
                    className="bg bg-green-500 text-white rounded-md px-4 py-4"
                >
                    Back to Top
                </button>
            </div>
        </form>
    )
}

バックエンド側(Django)の実装

データベース周りの設定

backend/todoList/setting.py
"""
Django settings for todoproject project.

Generated by 'django-admin startproject' using Django 5.1.7.

For more information on this file, see
https://docs.djangoproject.com/en/5.1/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.1/ref/settings/
"""
import os # 追加
from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-6@l&+js7s77)@o_tifmlf+^gvv*_&i(4m@2hal#7x+8nczn23h'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',#追加
    'api',#追加
    'corsheaders',#追加
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'corsheaders.middleware.CorsMiddleware',#追加
]

ROOT_URLCONF = 'todoproject.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'todoproject.wsgi.application'


# Database
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'NDjango',
        'USER':'postgres',
        'PASSWORD':'XXXXXX',
        'HOST':'localhost',
        'PORT':'5432',
    }
}


# Password validation
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

CORS_ALLOWED_ORIGINS = [#追加
    'http://localhost:3000',
]


# Internationalization
# https://docs.djangoproject.com/en/5.1/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.1/howto/static-files/

STATIC_URL = 'static/'

# Default primary key field type
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
MEDIA_URL = '/media/'#追加
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')#追加

Modelsクラスの実装

backend/api/models.py
from django.db import models

# Create your models here.
class Todo(models.Model):
    title = models.CharField(max_length=100)
    completed = models.BooleanField(default=False)
    attachment = models.FileField(upload_to='uploads/', blank=True, null=True)  # Optional file attachment

    def __str__(self):
        return self.title
    
class AdminUsers(models.Model):
    adminUserName = models.CharField(max_length=20,blank=False,null=False)
    adminUserEmail = models.CharField(max_length=50,blank=False,null=False)
    adminUserPhone = models.CharField(max_length=20,blank=False,null=False)
    adminUserPassword = models.CharField(max_length=100,blank=False,null=False)
    adminUserCompany = models.CharField(max_length=20,blank=False,null=False)
    adminUserDepartment = models.CharField(max_length=30,blank=False,null=False)

    def __str__(self):
        return self.adminUserName

Serializerクラスの実装

パスワードハッシュ化の実装について

パスワードをハッシュ化するcreate関数は、class Metaクラスと同じ階層に配置します。
階層の配置を間違えると、処理が正しく動作しないので注意してください。

backend/api/serializer.py
class AdminUsersSerializer(serializers.ModelSerializer):
    class Meta:
        model = AdminUsers
        fields = ['id','adminUserName','adminUserEmail','adminUserPhone','adminUserPassword','adminUserCompany','adminUserDepartment']
        adminUserPassword = serializers.CharField(write_only=True)

        extra_kwargs = {
            'adminUserPassword':{'write_only':True}
        }
   
    def create(self, validated_data):
        # パスワードをハッシュ化
        password = validated_data['adminUserPassword']
        hashed_password = make_password(password)
        validated_data['adminUserPassword'] = hashed_password
    
        # 新しいユーザーを作成
        return super().create(validated_data)
backend/api/serializer.py
from rest_framework import serializers
from .models import Todo,AdminUsers
from django.contrib.auth.hashers import make_password

class TodoSerializer(serializers.ModelSerializer):
    class Meta:
        model = Todo
        fields = ['id', 'title', 'completed', 'attachment']  # Include necessary fields

class AdminUsersSerializer(serializers.ModelSerializer):
    class Meta:
        model = AdminUsers
        fields = ['id','adminUserName','adminUserEmail','adminUserPhone','adminUserPassword','adminUserCompany','adminUserDepartment']
        adminUserPassword = serializers.CharField(write_only=True)

        extra_kwargs = {
            'adminUserPassword':{'write_only':True}
        }
   
    def create(self, validated_data):
        # パスワードをハッシュ化
        password = validated_data['adminUserPassword']
        hashed_password = make_password(password)
        validated_data['adminUserPassword'] = hashed_password
    
        # 新しいユーザーを作成
        return super().create(validated_data)
        

views.pyの実装

backend/api/views.py
#from django.shortcuts import render
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 rest_framework import viewsets
from .models import Todo,AdminUsers
from .serializers import TodoSerializer,AdminUsersSerializer


# Create your views here.
class TodoViewSet(viewsets.ModelViewSet):
    queryset = Todo.objects.all()
    serializer_class = TodoSerializer

class AdminUsersViewSet(viewsets.ModelViewSet):
    queryset = AdminUsers.objects.all()
    serializer_class = AdminUsersSerializer

@api_view(['POST'])
def create_admin_user(request):
    if request.method == 'POST':
        # Start transaction
        with transaction.atomic():
            serializer = AdminUsersSerializer(data=request.data)
            if serializer.is_valid():
                #Generate Password Hash
                password = serializer.validated_data['adminUserPassword']
                hashed_password = make_password(password)
                
                #Set hasedPassword to Data
                serializer.validated_data['adminUserPassword'] = hashed_password#hashed_password
                if serializer.is_valid():

                    serializer.save()
                    return Response(serializer.data,status=status.HTTP_201_CREATED)#serializer.data
            else:       
                return Response(serializer.errors,status=status.HTTP_400_BAD_REQUEST)#serializer.data
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?