[Django] Djangoのプロジェクト作成からデプロイまで

$ mkdir llm_tts_backend
$ cd llm_tts_backend

$ django-admin startproject config .
$ manage.py startapp api

requirements.txt

Django>=4.2.0
requests>=2.31.0
gTTS>=2.3.2
python-dotenv>=1.0.0
django-cors-headers>=4.3.0
EOF

$ pip3 install -r requirements.txt

.envファイルの作成
$ cat > .env << 'EOF' DIFY_API_KEY=your_dify_api_key_here APP_ID=your_app_id_here SECRET_KEY=django-insecure-your-secret-key-here DEBUG=True EOF django-insecure-your-secret-key-here は以下のように発行する $ python3 -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())' config/settings.py [code] import os from pathlib import Path from dotenv import load_dotenv load_dotenv() # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = os.environ.get('SECRET_KEY', 'django-insecure-default-key') # SECURITY WARNING: don't run with debug turned on in production! DEBUG = os.environ.get('DEBUG', 'False') == 'True' ALLOWED_HOSTS = ['*'] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'corsheaders', 'api', ] MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] CORS_ALLOWED_ORIGINS = [ "http://localhost:3000", "http://localhost:8080", "http://127.0.0.1:3000", "http://127.0.0.1:8080", "http://192.168.33.10:3000", "http://192.168.33.10:8080", ] CORS_ALLOW_CREDENTIALS = True CSRF_COOKIE_HTTPONLY = False CSRF_COOKIE_SAMESITE = 'Lax' ROOT_URLCONF = 'config.urls' [/code] api/views.py [code] from django.http import JsonResponse, FileResponse from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_http_methods from gtts import gTTS import requests import json import os import tempfile from datetime import datetime from dotenv import load_dotenv load_dotenv() API_KEY = os.environ.get("DIFY_API_KEY") BASE_URL = "https://api.dify.ai/v1" CHAT_ENDPOINT = f"{BASE_URL}/chat-messages" # Create your views here. @csrf_exempt @require_http_methods(["POST"]) def process_query(request): """ フロントエンドからテキストを受け取り、Dify LLM処理後、TTSで音声を返す """ try: data = json.loads(request.body) if not data or 'text' not in data: return JsonResponse({'error': 'text パラメータが必要です'}, status=400) user_text = data['text'] lang = data.get('lang', 'ja') slow = data.get('slow', False) if not user_text.strip(): return JsonResponse({'error': 'テキストが空です'}, status=400) # --- Dify LLM処理 --- payload = { "query": user_text, "inputs": {"context": "Null"}, "user": f"user_django_{datetime.now().strftime('%Y%m%d%H%M%S')}", "response_mode": "blocking", } headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json" } llm_response = requests.post( CHAT_ENDPOINT, headers=headers, data=json.dumps(payload), timeout=30 ) llm_response.raise_for_status() llm_data = llm_response.json() if not llm_data or 'answer' not in llm_data: return JsonResponse({'error': 'LLMからの回答が取得できませんでした'}, status=500) llm_answer = llm_data['answer'] # --- TTS処理 --- temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.mp3') temp_filename = temp_file.name temp_file.close() tts = gTTS(text=llm_answer, lang=lang, slow=slow) tts.save(temp_filename) response = FileResponse( open(temp_filename, 'rb'), content_type='audio/mpeg', as_attachment=True, filename=f'speech_{datetime.now().strftime("%Y%m%d_%H%M%S")}.mp3' ) response['X-LLM-Answer'] = llm_answer return response except requests.exceptions.RequestException as e: return JsonResponse({'error': f'LLM API エラー: {str(e)}'}, status=500) except Exception as e: return JsonResponse({'error': f'サーバーエラー: {str(e)}'}, status=500) @require_http_methods(["GET"]) def health_check(request): """ヘルスチェック用エンドポイント""" return JsonResponse({'status': 'ok'}) @require_http_methods(["GET"]) def get_languages(request): """サポートされている言語のリストを返す""" languages = { 'ja': '日本語', 'en': '英語', 'zh-cn': '中国語(簡体字)', 'zh-tw': '中国語(繁体字)', 'ko': '韓国語', 'es': 'スペイン語', 'fr': 'フランス語', 'de': 'ドイツ語', 'it': 'イタリア語', 'pt': 'ポルトガル語', } return JsonResponse(languages) [/code] api/urls.py [code] from django.urls import path from . import views urlpatterns = [ path('process', views.process_query, name='process_query'), path('health', views.health_check, name='health_check'), path('languages', views.get_languages, name='get_languages'), ] [/code] config/urls.py [code] from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('api/', include('api.urls')), ] [/code] ステップ11: データベースのマイグレーション $ python3 manage.py migrate ステップ12: 開発サーバ $ python3 manage.py runserver $ curl http://localhost:8000/api/health $ curl http://localhost:8000/api/languages00/api/languages {"ja": "\u65e5\u672c\u8a9e", "en": "\u82f1\u8a9e", "zh-cn": "\u4e2d\u56fd\u8a9e(\u7c21\u4f53\u5b57)", "zh-tw": "\u4e2d\u56fd\u8a9e(\u7e41\u4f53\u5b57)", "ko": "\u97d3\u56fd\u8a9e", "es": "\u30b9\u30da\u30a4\u30f3\u8a9e", "fr": "\u30d5\u30e9\u30f3\u30b9\u8a9e", "de": "\u30c9\u30a4\u30c4\u8a9e", "it": "\u30a4\u30bf\u30ea\u30a2\u8a9e", "pt": "\u30dd\u30eb\u30c8\u30ac\u30eb\u8a9e"} curl -X POST http://localhost:8000/api/process \ -H "Content-Type: application/json" \ -d '{"text": "こんにちは"}' \ --output test_response.mp3