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

Django-Allauth中Kakao登录出现MultipleObjectsReturned错误

解决Django-allauth接入Kakao登录时的MultipleObjectsReturned异常

问题背景

接入Django-allauth实现Kakao登录时,触发MultipleObjectsReturned或ObjectDoesNotExist异常,Google登录暂因版本问题优先排查Kakao。已按官方文档配置并尝试常规排障,问题未解决。

views.py 相关代码

def kakao_callback(request):
    code = request.GET.get("code")
    print(f"code : {code}")
    
    # ---- Access Token Request ----
    token_req = requests.get(
        f"https://kauth.kakao.com/oauth/token?grant_type=authorization_code&client_id={KAKAO_REST_API_KEY}&redirect_uri={KAKAO_REDIRECT_URI}&code={code}"
    )
    token_req_json = token_req.json()
    error = token_req_json.get("error", None)
    if error is not None:
        raise JSONDecodeError(f"Failed to decode JSON: {error}", '{"error": "your_error_message"}', 0)

    access_token = token_req_json.get("access_token")
    print(f"access_token : {access_token}")
    
    # ---- Email Request ----
    profile_request = requests.post(
        "https://kapi.kakao.com/v2/user/me",
        headers={"Authorization": f"Bearer {access_token}"},
    )
    profile_json = profile_request.json()
    error = profile_json.get("error", None)
    if error is not None:
        raise JSONDecodeError("Failed to decode JSON", '{"error": "your_error_message"}', 0)

    kakao_account = profile_json.get("kakao_account")
    email = kakao_account.get("email", None)
    profile = kakao_account.get("profile")
    nickname = profile.get("nickname")
    profile_image = profile.get("thumbnail_image_url")
    # print(email)
    
    # ---- Signup or Signin Request ----
    try:
        user = User.objects.get(email=email)
        social_user = SocialAccount.objects.get(user=user)
        if social_user is None:
            return JsonResponse(
                {"err_msg": "email exists but not kakao social user"},
                status=status.HTTP_400_BAD_REQUEST,
            )
        if social_user.provider != "kakao":
            return JsonResponse(
                {"err_msg": "no matching social type"},
                status=status.HTTP_400_BAD_REQUEST,
            )
            

        data = {"access_token": access_token, "code": code}
        accept = requests.post(f"{BASE_URL}accounts/kakao/login/finish/", data=data)
        accept_status = accept.status_code
        
        if accept_status != 200:
            print(f"Accept request status code: {accept.status_code}")
            return JsonResponse({"err_msg": "failed to signin_registered user."}, status=accept_status)
        accept_json = accept.json()
        accept_json.pop('user', None)
        refresh_token = accept.headers['Set-Cookie']
        refresh_token = refresh_token.replace('=',';').replace(',',';').split(';')
        token_index = refresh_token.index(' refresh_token')
        cookie_max_age = 3600 * 24 * 14 # 14 days
        refresh_token = refresh_token[token_index+1]
        response_cookie = JsonResponse(accept_json)
        response_cookie.set_cookie('refresh_token', refresh_token, max_age=cookie_max_age, httponly=True, samesite='Lax')
        return response_cookie
    
    except User.DoesNotExist:
        cookie_max_age = 3600 * 24 * 14
        data = {"access_token": access_token, "code": code}
        accept = requests.post(f"{BASE_URL}accounts/kakao/login/finish/", data=data)
        accept_status = accept.status_code  
    
        if accept_status != 200:
            print(f"Failed to signup_new user. Status code: {accept_status}")
            return JsonResponse({"err_msg": "failed to signup_new user"}, status=accept_status)

        accept_json = accept.json()
        accept_json.pop('user', None)
        refresh_token = accept.headers['Set-Cookie']
        refresh_token = refresh_token.replace('=',';').replace(',',';').split(';')
        token_index = refresh_token.index(' refresh_token')
        refresh_token = refresh_token[token_index+1]

        response_cookie = JsonResponse(accept_json)
        response_cookie.set_cookie('refresh_token', refresh_token, max_age=cookie_max_age, httponly=True, samesite='Lax')
        return response_cookie
    
    except JSONDecodeError as e:
        print(f"JSONDecodeError: {e}")
        raise
    
    except User.MultipleObjectsReturned as e:
        print(f"MultipleObjectsReturned: {e}")
        return JsonResponse(
            {"err_msg": "MultipleObjectsReturned. Check logs for details."},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR,
        )
    
    except:
        return JsonResponse({"MESSAGE":"KEY_ERROR"}, status=400)

数据库核查情况

已确认SocialApp表中存在Kakao和Google两条记录,provider对应的client_id配置正确,执行SocialApp.objects.all()返回:

<QuerySet [<SocialApp: Kakao>, <SocialApp: google>]>

settings.py 中 SOCIALACCOUNT_PROVIDERS 配置

SOCIALACCOUNT_PROVIDERS = {
    'kakao': {
        'APP': {
            'client_id': KAKAO_SECRET_KEY,
            'secret': KAKAO_REST_API_KEY,
        }
    },
    'google': {
        'APP':{
            'clinet_id':secrets['SOCIAL_AUTH_GOOGLE_CLIENT_ID'],
            'secret': secrets['SOCIAL_AUTH_GOOGLE_SECRET']
            },
    }
}

错误追踪日志

File "C:\Users\user\anaconda3\envs\Pj\Lib\site-packages\allauth\socialaccount\adapter.py", line 290, in get_app
    raise MultipleObjectsReturned
django.core.exceptions.MultipleObjectsReturned

问题分析与解决方案

核心原因

异常来自allauth的get_app方法,说明系统查询SocialApp时返回了多个匹配结果,结合现有配置和代码,问题点集中在:

  1. provider名称大小写不统一:数据库中Kakao的provider是Kakao(首字母大写),但配置和代码中用的是小写kakao,导致查询逻辑混乱。
  2. Kakao配置参数颠倒:client_id和secret取值搞反,不符合官方要求。
  3. Google配置拼写错误:clinet_id应为client_id,可能引发配置加载异常间接影响Kakao查询。
  4. views中社交账号查询逻辑不严谨:未按provider过滤,同一用户绑定多个社交账号时会触发异常。

修复步骤

  1. 统一provider名称
    修改数据库中Kakao的provider字段为小写kakao,确保与配置、代码中的名称完全一致。

  2. 修正Kakao配置参数

    SOCIALACCOUNT_PROVIDERS = {
        'kakao': {
            'APP': {
                'client_id': KAKAO_REST_API_KEY,  # 对应Kakao的REST API Key
                'secret': KAKAO_SECRET_KEY,       # 对应Kakao的Client Secret
            }
        },
        # ...其他配置
    }
    
  3. 修复Google配置拼写错误

    'google': {
        'APP':{
            'client_id': secrets['SOCIAL_AUTH_GOOGLE_CLIENT_ID'],  # 修正clinet_id为client_id
            'secret': secrets['SOCIAL_AUTH_GOOGLE_SECRET']
        },
    }
    
  4. 优化views查询逻辑
    将SocialAccount.objects.get(user=user)修改为按provider过滤,同时移除冗余判断:

    # 替换原social_user查询语句
    social_user = SocialAccount.objects.get(user=user, provider='kakao')
    # 移除if social_user is None判断,因为get方法要么返回对象要么抛异常
    

内容的提问来源于stack exchange,提问作者hyunsu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 00:27:34