Django实现Google Calendar API OAuth2授权的完整示例求助
Django 集成Google Calendar API 完整实现方案
前置依赖安装
执行命令安装所需依赖:pip install django google-api-python-client google-auth-httplib2 google-auth-oauthlib python-dotenv
1. 基础配置
1.1 Google Cloud 端配置
- 创建新项目,搜索并启用
Google Calendar API - 进入「API和服务」-「凭据」页面,创建OAuth 2.0客户端ID,应用类型选择Web应用
- 授权重定向URI填写你的Django应用回调地址,本地开发可填
http://localhost:8000/google/callback/ - 生成后保存
client_id和client_secret两个参数
1.2 Django 项目配置
在settings.py中添加以下配置,敏感参数建议通过环境变量读取:
import os from dotenv import load_dotenv load_dotenv() GOOGLE_OAUTH2 = { "CLIENT_ID": os.getenv("GOOGLE_CLIENT_ID"), "CLIENT_SECRET": os.getenv("GOOGLE_CLIENT_SECRET"), "REDIRECT_URI": "http://localhost:8000/google/callback/", "SCOPES": ["https://www.googleapis.com/auth/calendar"] } # 确保session配置正常启用,用于存储用户授权凭证 SESSION_ENGINE = "django.contrib.sessions.backends.db"
2. 核心路由配置
在应用的urls.py中添加路由:
from django.urls import path from . import views urlpatterns = [ path("google/auth/", views.google_auth, name="google_auth"), path("google/callback/", views.google_callback, name="google_callback"), path("calendar/event/operate/", views.operate_calendar_event, name="operate_calendar_event"), ]
3. 核心视图逻辑实现
在views.py中编写完整授权+日历操作逻辑:
from google.oauth2.credentials import Credentials from google_auth_oauthlib.flow import Flow from googleapiclient.discovery import build from googleapiclient.errors import HttpError from django.conf import settings from django.shortcuts import redirect from django.http import JsonResponse def get_google_credentials(request): """从session中获取用户授权凭证,无凭证则跳转授权页""" cred_data = request.session.get("google_credential") if not cred_data: return None return Credentials( token=cred_data["token"], refresh_token=cred_data["refresh_token"], token_uri=cred_data["token_uri"], client_id=cred_data["client_id"], client_secret=cred_data["client_secret"], scopes=cred_data["scopes"] ) def google_auth(request): """生成Google授权跳转链接,引导用户授权""" flow = Flow.from_client_config( client_config={ "web": { "client_id": settings.GOOGLE_OAUTH2["CLIENT_ID"], "client_secret": settings.GOOGLE_OAUTH2["CLIENT_SECRET"], "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token" } }, scopes=settings.GOOGLE_OAUTH2["SCOPES"] ) flow.redirect_uri = settings.GOOGLE_OAUTH2["REDIRECT_URI"] # 离线访问模式才能获取refresh_token,prompt=consent强制每次授权都返回refresh_token authorization_url, state = flow.authorization_url( access_type="offline", include_granted_scopes="true", prompt="consent" ) # 存储state用于后续回调校验,防止CSRF request.session["google_auth_state"] = state return redirect(authorization_url) def google_callback(request): """处理Google授权回调,兑换并存储访问凭证""" state = request.session.get("google_auth_state") if not state or request.GET.get("state") != state: return JsonResponse({"error": "无效的授权回调请求"}, status=400) flow = Flow.from_client_config( client_config={ "web": { "client_id": settings.GOOGLE_OAUTH2["CLIENT_ID"], "client_secret": settings.GOOGLE_OAUTH2["CLIENT_SECRET"], "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token" } }, scopes=settings.GOOGLE_OAUTH2["SCOPES"], state=state ) flow.redirect_uri = settings.GOOGLE_OAUTH2["REDIRECT_URI"] # 用回调返回的授权码兑换访问凭证 flow.fetch_token(authorization_response=request.build_absolute_uri()) credentials = flow.credentials # 凭证存入session,生产环境建议存入数据库关联用户 request.session["google_credential"] = { "token": credentials.token, "refresh_token": credentials.refresh_token, "token_uri": credentials.token_uri, "client_id": credentials.client_id, "client_secret": credentials.client_secret, "scopes": credentials.scopes } # 授权完成后跳转到日历操作页面 return redirect("operate_calendar_event") def operate_calendar_event(request): """日历事件增删改示例接口""" credentials = get_google_credentials(request) if not credentials: return redirect("google_auth") try: service = build("calendar", "v3", credentials=credentials) # --------------- 1. 创建事件示例 --------------- event = { "summary": "测试会议", "location": "北京市朝阳区", "description": "这是通过Django集成创建的日历会议", "start": { "dateTime": "2024-06-01T10:00:00+08:00", "timeZone": "Asia/Shanghai", }, "end": { "dateTime": "2024-06-01T12:00:00+08:00", "timeZone": "Asia/Shanghai", }, "attendees": [ {"email": "user1@example.com"}, {"email": "user2@example.com"}, ], "reminders": { "useDefault": False, "overrides": [ {"method": "email", "minutes": 24 * 60}, {"method": "popup", "minutes": 10}, ], } } created_event = service.events().insert(calendarId="primary", body=event).execute() event_id = created_event["id"] # --------------- 2. 更新事件示例 --------------- event["summary"] = "更新后的测试会议" updated_event = service.events().update(calendarId="primary", eventId=event_id, body=event).execute() # --------------- 3. 删除事件示例 --------------- # service.events().delete(calendarId="primary", eventId=event_id).execute() return JsonResponse({ "status": "success", "created_event_link": created_event.get("htmlLink"), "updated_event_summary": updated_event.get("summary") }) except HttpError as e: return JsonResponse({"error": f"日历操作失败:{str(e)}"}, status=500)
4. 注意事项
- 只有首次用户授权时会返回
refresh_token,如果后续丢失需要在授权参数中添加prompt="consent"强制用户重新授权获取 - 凭证默认1小时过期,谷歌官方SDK会自动使用
refresh_token刷新凭证,无需手动处理 - 生产环境中用户授权凭证建议关联用户ID存储到数据库,不要仅存在session中
- 生产环境需要配置HTTPS,重定向URI也要对应修改为HTTPS地址
- 敏感的
client_id、client_secret不要硬编码到代码中,建议通过环境变量或者加密配置存储
内容的提问来源于stack exchange,提问作者Ioana Sabau
相关产品推荐
相关产品推荐

