Django REST Frameworkおよびajax通信を使用して、フォームに入力した内容を、ログインしているユーザー情報と共にデータベースに登録する方法をご紹介したいと思います。
動作環境
下記の内容で動作を確認しております。
- python : 3.9.10
- Django : 4.2.5
- djangorestframework : 3.14.0
まえがき
実際に機能をご紹介していきますが、Djangoのプロジェクトやアプリの作成方法などは割愛させていただきます。
Djangoを使用した最小限のアプリ作成までの方法などはこちらの記事をご覧いただけたらと思います。
またユースケースとして、フォームを使用してユーザーの好きな食べ物を登録できるページを考えていきます。
また、ログインしているユーザーと紐付けを行いますので、ユーザーによって情報の抽出を行うことができます。
実装
必要なライブラリのインストール
Django REST Frameworkを使用しますので、下記のコマンドを実行して必要なライブラリをインストールします。
pip install djangorestframework==3.14.0
さらに、このライブラリの機能を使用できるようにするため、settings.pyに追記します。
INSTALLED_APPS = [
...
'rest_framework',
]
カスタムユーザーモデル
本記事では、django-allauthおよびカスタムユーザーモデルを定義して、認証機能を実装していると想定します。
django-allauthを使用した認証機能の実装方法は、こちらの記事をご覧いただけたらと思います。
こちらでは、プロジェクト内に「accounts」というアプリを追加し、さらに下記のようなカスタムユーザーモデルを定義しています。
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
""" カスタムユーザーモデル """
class Meta:
verbose_name_plural = 'CustomUser'
models.py
models.pyには、登録する予定の料理に関するクラスを定義します。
from django.db import models
from accounts.models import CustomUser
class Food(models.Model):
user = models.ForeignKey(CustomUser, verbose_name='ユーザー', on_delete=models.PROTECT)
name = models.CharField(verbose_name='料理名', max_length=255, primary_key=True)
created_at = models.DateTimeField(verbose_name='更新日時', auto_now=True)
class Meta:
verbose_name_plural = 'Food'
def __str__(self):
return self.name
ユーザーを外部キーとしています。
主キーが料理名というクソ仕様ですが、サンプルのためご了承ください。
忘れずにマイグレーションを行います。
python manage.py makemigrations
python manage.py migrate
シリアライザ
次にDRF(Django REST Framework)を使用するのに必要なシリアライザを定義していきます。
シリアライザを使用し、フォーム送信された値のバリデーションを行うことができます。
今回は必要最低限な項目のみで構成しております。
from rest_framework import serializers
from .models import Food
from accounts.models import CustomUser
class FoodSerializer(serializers.ModelSerializer):
created_at = serializers.DateTimeField(format='%Y-%m-%d', read_only=True)
class Meta:
model = Food
fields = ('name', 'created_at')
extra_kwargs = {'user': {'write_only': True}}
ビュー
ビューを作成します。
from rest_framework import generics
from .models import Food
from .serializer import FoodSerializer
class FoodCreateView(generics.CreateAPIView):
queryset = Food.objects.all()
serializer_class = FoodSerializer
def perform_create(self, serializer):
serializer.save(user=self.request.user)
generics.CreateAPIViewを継承し、およびここまでで作成したモデルとシリアライザを使用し上記のように記述するだけで、Foodモデルにレコードを追加するビューが作成できます。
またperform_create()メソッドをオーバーライドし、フォーム送信時に一緒にユーザー情報を付与するようにしています。
ルーティング
最後にルーティング設定を行います。
from django.urls import path
from . import views
app_name = 'app_name'
urlpatterns = [
...,
path('api/create-food/', views.FoodCreateView.as_view(), name="create_food"),
]
ここまで設定を行ったところで、実際に機能するのか確認してみます。
開発環境にて、http://127.0.0.1:8000/api/create-food/ にアクセスしてみます。
このように、Foodテーブルに項目を追加できるページが表示されるはずです。
お好きな料理名を入力し「POST」ボタンを押下することで、実際にレコードを挿入することができます。
次からは、この機能を実際のアプリケーション内に組み込んでいきます。
テンプレート
作成したFoodモデルには、下記の3つの項目がありました。
- user
- name
- created_at
「user」はviews.pyで、「created_at」は作成時に自動で補填されますので、ユーザーが入力する必要があるのは「name」のみであることが分かります。
したがって、任意のテンプレート内に、下記のようなフォームを作成しましょう。
<form method="post" action="" id="form__create-food">
{% csrf_token %}
<input type="text" name="name" id="name">
<button id="btn__create-food" type="button">追加</button>
</form>
また同一のテンプレート内に、下記のようなjsスクリプトを追加します。
<script>
const submitBtn = document.getElementById('btn__create-food');
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i].trim();
// クッキー名からCSRFトークンを探す
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
submitBtn.addEventListener('click', () => {
let name = document.getElementById('name').value;
let createEndPoint = "/api/create-food/";
formData = {
'name': name
}
fetch(createEndPoint, {
method: 'POST',
headers: {
'X-CSRFToken': getCookie('csrftoken'),
'Content-Type': 'application/json',
},
body: JSON.stringify(formData)
})
.then(res => {
if (res.ok) {
console.log('Successfully add food.');
} else {
console.error('Error occured during sending data.');
}
})
.catch(error => {
console.error('Network error:', error);
});
});
</script>
テンプレート内に設置したボタンにオンクリックイベントとして、フォーム内容をAjax通信でPOSTするメソッドを定義しています。
このようにすることで、ページ遷移を行うことなくデータベースへレコードを挿入することができます。
もしよりよい方法や、ご指摘などございましたら、ぜひご教示いただけると幸いです。
コメント