You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.23 22:45:05