⛩️
AI参拝ナビを作る #1:Django REST Frameworkで検索APIを実装する
はじめに
「AI参拝ナビ」という神社アプリを開発しています。
MVP(最小実用プロダクト)として、まずは「神社を検索できるAPI」をDjango REST Frameworkで実装しました。
- バックエンド: Django + DRF + PostgreSQL (Docker)
- フロントエンド: Next.js + Expo
- 地図: Google Maps API / Mapbox
- AI: OpenAI Responses API
今回は 検索API 実装の工程を紹介します。
モデル定義
神社 (Shrine) とご利益タグ (GoriyakuTag) をManyToManyで関連付けています。
class GoriyakuTag(models.Model):
name = models.CharField(max_length=50, unique=True)
class Shrine(models.Model):
name_jp = models.CharField(max_length=100)
name_romaji = models.CharField(max_length=100, blank=True, null=True)
address = models.CharField(max_length=255)
latitude = models.FloatField()
longitude = models.FloatField()
goriyaku_tags = models.ManyToManyField(GoriyakuTag, related_name="shrines")
ご利益タグはマスターデータとして 15種類をfixturesから投入。
例: 「縁結び」「金運・商売繁盛」「健康長寿」など。
APIエンドポイント
/api/goriyaku-tags/
ご利益タグ一覧を返す。
[{"id":1,"name":"縁結び"},{"id":5,"name":"金運・商売繁盛"}]
/api/shrines/
神社一覧(ご利益タグも含む)。
さらにクエリパラメータで検索に対応。
タグ検索: ?tag=縁結び
名前検索: ?name=明治
現在地検索: ?lat=35.66&lng=139.70&radius=5000
半径検索の実装
PostGISは使わず、まずはシンプルに ハーサイン式 (haversine formula) で距離を計算しました。
from math import radians, cos, sin, asin, sqrt
def haversine(lon1, lat1, lon2, lat2):
lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])
dlon, dlat = lon2 - lon1, lat2 - lat1
a = sin(dlat/2)**2 + cos(lat1)*cos(lat2)*sin(dlon/2)**2
return 6371000 * 2 * asin(sqrt(a)) # 地球半径6371km
ViewSetで利用:
if lat and lng:
ids = []
for shrine in queryset:
distance = haversine(lng, lat, shrine.longitude, shrine.latitude)
if distance <= radius:
ids.append(shrine.id)
queryset = queryset.filter(id__in=ids)
ハマりどころ
curlで日本語検索が通らない → ?tag=縁結び はエンコード必須
ShrineViewSetが2重定義 → 後ろで上書きされてフィルタ無効になっていた
querysetをリスト化するとfilterできない → filter(id__in=ids) に修正
ブランチ運用
feature/search-api → タグ・名前検索を実装
feature/location-search → 半径検索を追加
PR単位で進めることで安全に開発を進められました。
実行例
渋谷(lat=35.66, lng=139.70, 半径5km)で検索:
curl "http://localhost:8000/api/shrines/?lat=35.66&lng=139.70&radius=5000"
結果:
[
{
"id": 1,
"name_jp": "明治神宮",
"goriyaku_tags": [{"id":1,"name":"縁結び"}]
}
]
京都の伏見稲荷大社は返ってこず、正しくフィルタされました ✅
次のステップ
フロントエンド(Next.js)でAPIを呼び出し、地図にマーカー表示
コンシェルジュ機能(干支/四柱推命ベースの提案)
ルート検索(Mapbox Directions API連携)
おわりに
MVP開発では「動くものを小さく作り、PR単位で積み重ねる」ことを意識しました。
次回は フロントエンドでの検索UI実装 を記事にまとめます!
---
Discussion