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から取得したデータに基づいて有効かどうかを検証する場合にも使用されます。

 

・カスタムエラーメッセージの提供

標準のエラーメッセージではなく、より具体的でユーザーフレンドリーなフィードバックを提供したい場合に役立ちます。