.md ファイルの取込みと JSON 化の概要

ウェブアプリの設計図

日付:2025年2月26日

Markdown ファイルを Django DRF にアップロードし、JSON API に変換するプロセスについて詳しく解説します。

目 次

1. はじめに

前回の記事では、Next.js と Markdown で運用していたブログを Django REST Framework (DRF) を用いて API 化する背景について解説しました。
今回は、その最初のステップとして、Markdown ファイルを Django にバルクアップロードし、JSON API に変換するプロセスについて詳しく説明します。

この機能を導入することで、記事データの管理が容易になり、フロントエンドは API からデータを取得するだけで動作できるようになります。
Markdown ファイルをアップロードし、API で取得できる形にすることで、フロントエンドの負担を軽減し、システムの柔軟性を向上させることが目的です。

2. Markdown ファイルのバルクアップロード機能

2.1 アップロードの流れ

Django 側で Markdown ファイルを受け取り、データベースに保存するまでの流れは次の通りです。

  1. 管理画面から Markdown ファイルをアップロード
  2. フロントマター (Front Matter) を解析し、記事情報(タイトル、日付、カテゴリ、テーマなど)を抽出
  3. データベースに保存し、API で取得可能にする
  4. データの整合性を確保するためのバリデーションを実施
  5. OpenAI Embedding API との連携を準備(次回以降で解説)

これを Django Admin のカスタムビューを作成することで実装しました。

2.2 Django Admin でのアップロード

Django の管理画面から Markdown ファイルをアップロードできるようにするため、カスタムビューを作成しました。

admin/upload_views.py

PYTHON
import frontmatter from django.shortcuts import render, redirect from django.db import transaction from django.contrib import messages from django.utils.text import slugify from base.forms import BulkUploadForm from base.models import Post, Category, Theme from base.embedding_utils import embed_full_text def upload_view(request): if request.method == "POST": form = BulkUploadForm(request.POST, request.FILES) if form.is_valid(): files = request.FILES.getlist("file") # ファイル数の上限チェック(例: 最大10個まで) if len(files) > 10: messages.error(request, "ファイルは最大10個までアップロード可能です。") return redirect("admin:base_post_changelist") for uploaded_file in files: # ファイルオブジェクトなら read() してバイト列を取得 if hasattr(uploaded_file, 'read'): file_data = uploaded_file.read() else: file_data = uploaded_file # ファイル名から拡張子を除いた部分を slug として取得 filename = uploaded_file.name # 例: "my-post.md" file_slug = slugify(filename.rsplit('.', 1)[0]) # "my-post" # バイト列をUTF-8でデコードして文字列に変換 text = file_data.decode("utf-8") parsed = frontmatter.loads(text) metadata = parsed.metadata content = parsed.content with transaction.atomic(): # 必須項目チェックなど省略 custom_id_val = metadata["id"] try: post = Post.objects.get(custom_id=custom_id_val) except Post.DoesNotExist: post = Post(custom_id=custom_id_val) post.title = metadata["title"] post.date = metadata["date"] post.description = metadata.get("description", "") post.content = content post.is_pinned = metadata.get("isPinned", False) # category の処理: 文字列から Category オブジェクトを取得または作成 category_name = metadata.get("category", "").strip() if category_name: category_obj, created = Category.objects.get_or_create(name=category_name) post.category = category_obj else: post.category = None # theme の処理も同様に theme_name = metadata.get("theme", "").strip() if theme_name: theme_obj, created = Theme.objects.get_or_create(name=theme_name) post.theme = theme_obj else: post.theme = None # 新しく生成した slug を post.slug にセット post.slug = file_slug post.save() # 埋め込み生成 embedding = embed_full_text(post.content) post.embedding = embedding post.save() messages.success(request, "Markdownファイルのアップロードが完了しました。") return redirect("admin:base_post_changelist") else: form = BulkUploadForm() context = { "form": form, "title": ".md ファイル アップロード(フロントマター解析&Embedding)", "opts": Post._meta, } return render(request, "admin/upload.html", context)

このコードでは、アップロードされた Markdown ファイルをパースし、フロントマターから必要な情報を抽出してデータベースに保存する仕組みを構築しています。

3. Django DRF による API 化

3.1 シリアライザーの作成

Django では、モデルのデータを JSON 形式で出力するために Serializer を定義します。

serializers.py

PYTHON
from rest_framework import serializers from base.models import Post class PostSerializer(serializers.ModelSerializer): category_name = serializers.CharField(source="category.name", read_only=True) theme_name = serializers.CharField(source="theme.name", read_only=True) class Meta: model = Post fields = ("custom_id", "title", "date", "description", "content", "category_name", "theme_name", "slug")

3.2 API の設計

API では、記事データを提供する PostListViewSet を定義し、以下の機能を実装しました。

  • ページネーション対応(1 ページあたり 50 件のデータを取得可能)
  • 検索機能の実装(記事タイトルや説明文、カテゴリ、テーマで検索)
  • シリアライザーの切り替え(一覧取得と詳細取得で異なるフォーマットを提供)

3.3 PostListViewSet の実装

以下のように、Django REST Framework の viewsets.ReadOnlyModelViewSet を利用して、記事データを取得できる API を作成しました。

PYTHON
from rest_framework import viewsets, filters from rest_framework.pagination import PageNumberPagination from base.models import Post from base.serializers import PostListSerializer, PostDetailSerializer class StandardResultsSetPagination(PageNumberPagination): page_size = 50 page_size_query_param = 'page_size' max_page_size = 100 class PostListViewSet(viewsets.ReadOnlyModelViewSet): queryset = Post.objects.all().order_by("-custom_id", "-date") lookup_field = "slug" pagination_class = StandardResultsSetPagination # ページネーションの適用 filter_backends = [filters.SearchFilter] # 検索機能の有効化 search_fields = ['title', 'description', 'category__name', 'theme__name'] # 検索対象フィールド def get_serializer_class(self): if self.action == 'retrieve': return PostDetailSerializer return PostListSerializer

この API により、/api/posts/ から記事一覧を取得でき、検索やページネーションにも対応可能になりました。

4. まとめ

今回は、Django DRF を用いて Markdown ファイルをアップロードし、API 化する仕組み について詳しく解説しました。

この機能により、フロントエンドでは Next.js からシンプルに API を呼び出すだけでデータを取得でき、システムのスケーラビリティが向上しました。