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时返回了多个匹配结果,结合现有配置和代码,问题点集中在:
- provider名称大小写不统一:数据库中Kakao的provider是
Kakao(首字母大写),但配置和代码中用的是小写kakao,导致查询逻辑混乱。 - Kakao配置参数颠倒:
client_id和secret取值搞反,不符合官方要求。 - Google配置拼写错误:
clinet_id应为client_id,可能引发配置加载异常间接影响Kakao查询。 - views中社交账号查询逻辑不严谨:未按provider过滤,同一用户绑定多个社交账号时会触发异常。
修复步骤
统一provider名称
修改数据库中Kakao的provider字段为小写kakao,确保与配置、代码中的名称完全一致。修正Kakao配置参数
SOCIALACCOUNT_PROVIDERS = { 'kakao': { 'APP': { 'client_id': KAKAO_REST_API_KEY, # 对应Kakao的REST API Key 'secret': KAKAO_SECRET_KEY, # 对应Kakao的Client Secret } }, # ...其他配置 }修复Google配置拼写错误
'google': { 'APP':{ 'client_id': secrets['SOCIAL_AUTH_GOOGLE_CLIENT_ID'], # 修正clinet_id为client_id 'secret': secrets['SOCIAL_AUTH_GOOGLE_SECRET'] }, }优化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
相关产品推荐
相关产品推荐

