API CRUD処理とバリデーションの取り方
urls
from django.urls import path
from core import views
app_name = "core" ※名前空間(ディレクトリを表している)
urlpatterns = [
path("", views.index, name="index"),
path("tag/<str:tag_name>/", views.entry_list_per_tag, name="entry_list_per_tag"),
path("api/entry/search", views.api_search_entry, name="search_entry"),
path("api/entry/", views.api_entry, name="api_entry"),
path("api/entry/recent_list", views.api_recent_entry_list, name="api_recent_entry_list"),
]
models
from datetime import datetime
from django.db import models
from django.db.models import Q
#新しいデータモデルを作成している
class Tag(models.Model):
""" タグ """
#nameというフィールド(カラム)を作成している。
unique=Trueとは一意のものであるという意味
name = models.CharField("名称", max_length=64, unique=True)
@classmethod
def get_or_create(cls, name):
""" 指定された名称のタグを生成して返す、既にあればそれを取得して返す """
ret = cls.objects.filter(name=name).first()
if not ret:
ret = cls.objects.create(name=name)
return ret
@classmethod
#タグを作成している。
def multi_get_or_create(cls, names):
if not names:
return []
tags = []
for name in names:
tags.append(cls.get_or_create(name))
return tags
def __str__(self):
return self.name
class Meta:
db_table = "tag"
※def get_or_create(cls, name):
このメソッドは、指定された名前のタグをデータベースから検索し、存在すればそのオブジェクトを返します。もし存在しなければ、新しくその名前のタグを作成してから返します。
※def multi_get_or_create(cls, names):
このメソッドは、複数のタグ名をリストで受け取り、それぞれについてget_or_create
メソッドを呼び出して、タグのリストを返します。タグが存在しない場合は新しく作成します。
#Entryモデルを作成している
class Entry(models.Model):
""" 記事 """
day = models.DateField("日")
title = models.CharField("タイトル", max_length=64)
content = models.TextField("内容")
tags = models.ManyToManyField(Tag)
created_at = models.DateTimeField("登録日時", default=datetime.now)
@classmethod
def search(cls, keyword):
if not keyword:
return []
return cls.objects.filter(Q(title__contains=keyword) | Q(content__contains=keyword))
def to_dict(self, include_fields=["id", "day", "title", "content"]):
ret = {}
if "id" in include_fields:
ret["id"] = self.id,
if "day" in include_fields:
ret["day"] = f"{self.day:%Y-%m-%d}"
if "title" in include_fields:
ret["title"] = self.title
if "content" in include_fields:
ret["content"] = self.content
if "tags" in include_fields:
ret["tags"] = [tag.name for tag in self.tags.all()]
return ret
def __str__(self):
return f"{self.id}:{self.title}"
class Meta:
db_table = "entry"
ordering = ("-day",)
forms
from django import forms
from core.models import Entry
API_TOKEN = "snM9cKBEfmXpnxUNbQMBeimd"
def create_error_dict_from_form_errors(form_errors):
""" formのエラーを辞書形式に変換する
JSON形式でレスポンスに返すために利用される想定
"""
#エラーを入れる辞書を作成する
ret = {}
#引数のform_errorsからキーとバリューを取得する
for field_name, errors in form_errors.items():
#filed_nameのキーのバリューをリストにする
ret[field_name] = []
#errosからerrorがあれば、field_nameのリストに追加していく
for error in errors:
ret[field_name].append(error)
return ret
なぜfield_nameの値をリストにしているのかというと、エラーメッセージが2つ以上ある場合に、valueだと入らないので、2つ以上入れられるようにリストにしている。
{
"name": ["名前は必須です"],
"email": ["有効なメールアドレスを入力してください", "このメールアドレスは既に使用されています"]
}
顧客登録のバリデーション
class RegisterEntryAPIForm(forms.ModelForm):
""" 顧客登録APIのバリデーションForm """
#フォームにtoken
という名前の文字列フィールドを追加しています
token = forms.CharField()
#clean_field<name>というバリデーション(下記に説明)
def clean_content(self):
if not self.cleaned_data.get("content"):
return
#20文字以内ならエラーを返す
if len(self.cleaned_data["content"]) < 20:
#エラー文字
raise forms.ValidationError("内容は20文字以上で入力してください")
return self.cleaned_data["content"]
def clean_token(self):
if self.cleaned_data.get("token") != API_TOKEN:
return forms.ValidationError("不正なトークンです")
class Meta:
#どこのモデルと接続しているのか
model = Entry
#フォームに表示するフィールドを記述
fields = (
"day",
"title",
"content",
)
clean_<fieldname>()
メソッドというバリデーションについて
・特定の条件下でのバリデーション:
あるフィールドの値が特定の条件を満たす必要がある場合(例: 文字数制限、特定の形式やパターンの遵守)に使われます。
・複数フィールド間の依存関係のチェック:
例えば、パスワードとパスワード確認フィールドが一致するかをチェックする場合や、あるフィールドの値が別のフィールドの値に基づいて決定される必要がある場合などです。
・データベースや外部APIとの相互作用に基づくバリデーション:
入力された値がデータベース内の既存のレコードと重複していないか、または外部APIから取得したデータに基づいて有効かどうかを検証する場合にも使用されます。
・カスタムエラーメッセージの提供:
標準のエラーメッセージではなく、より具体的でユーザーフレンドリーなフィードバックを提供したい場合に役立ちます。