DigitalOcean生产环境中DjangoRestFramework注册登录报500错误
Django部署至DigitalOcean App Platform后注册/登录500错误排查
问题描述
按照DigitalOcean官方文档将Django应用部署至App Platform,使用DigitalOcean Spaces托管静态与媒体文件,生产环境下其他请求均正常,但注册(Signup)与登录(Login)功能仅在本地可用,服务器触发500内部错误。
配置信息
settings.py
BASE_DIR = Path(__file__).resolve().parent.parent SECRET_KEY = os.getenv("DJANGO_SECRET_KEY", get_random_secret_key()) # set debug to false by default DEBUG = os.getenv("DEBUG", "False") == "True" ALLOWED_HOSTS = os.getenv( "DJANGO_ALLOWED_HOSTS", "www.aklalatshop.com,https://akalat-shopcsphu.ondigitalocean.app").split(",") # Application definition INSTALLED_APPS = [ # 'whitenoise.runserver_nostatic', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.sites', 'egystoreapp', # For generating / getting access token 'oauth2_provider', 'social_django', ######################################## 'allauth', 'allauth.account', 'allauth.socialaccount', 'allauth.socialaccount.providers.facebook', # for social login 'rest_framework_social_oauth2', # for digitalOcean space 'storages', 'bootstrap4', 'rest_framework', 'rest_framework.authtoken', ] SITE_ID = 1 MIDDLEWARE = [ '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', 'whitenoise.middleware.WhiteNoiseMiddleware', 'social_django.middleware.SocialAuthExceptionMiddleware', ] ROOT_URLCONF = 'egystores.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', 'django.template.context_processors.media', 'social_django.context_processors.backends', 'social_django.context_processors.login_redirect', ], }, }, ] WSGI_APPLICATION = 'egystores.wsgi.application' DEVELOPMENT_MODE = os.getenv("DEVELOPMENT_MODE", "False") == "True" if DEVELOPMENT_MODE is True: DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": os.path.join(BASE_DIR, "db.sqlite3"), } } elif len(sys.argv) > 0 and sys.argv[1] != 'collectstatic': if os.getenv("DATABASE_URL", None) is None: raise Exception("DATABASE_URL environment variable not defined") DATABASES = { "default": dj_database_url.parse(os.environ.get("DATABASE_URL")), } AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.AllowAny', 'oauth2_provider.contrib.rest_framework.OAuth2Authentication', 'rest_framework_social_oauth2.authentication.SocialAuthentication', ], } OAUTH2_PROVIDER = { # this is the list of available scopes 'SCOPES': {'read': 'Read scope'}, 'ACCESS_TOKEN_EXPIRE_SECONDS': 36000, } OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL = "oauth2_provider.AccessToken" OAUTH2_PROVIDER_APPLICATION_MODEL = "oauth2_provider.Application" OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL = "oauth2_provider.RefreshToken" OAUTH2_PROVIDER_ID_TOKEN_MODEL = "oauth2_provider.IDToken" # configurations for digitalocean spaces AWS_ACCESS_KEY_ID = '******************' AWS_SECRET_ACCESS_KEY = '******************' AWS_STORAGE_BUCKET_NAME = '******************' AWS_S3_ENDPOINT_URL = '******************' AWS_S3_OBJECT_PARAMETERS = { 'CacheControl': 'max-age=86400', } AWS_DEFAULT_ACL = 'public-read' AWS_S3_SIGNATURE_VERSION = 's3v4' STATIC_URL = 'https://%s/%s/' % (AWS_S3_ENDPOINT_URL, 'staticfiles') MEDIA_URL = 'https://%s/%s/' % (AWS_S3_ENDPOINT_URL, 'mediafiles') STATICFILES_STORAGE = 'custom_storages.StaticStorage' DEFAULT_FILE_STORAGE = 'custom_storages.MediaStorage'
Serializers.py
class UserSerializer(serializers.ModelSerializer): # The Meta class specifies that we are interested class Meta: model = User fields = ["id", "first_name", "last_name", "username", "password", "email"] extra_kwargs = {'password': {'write_only': True}}
apis.py
from rest_framework.permissions import AllowAny from rest_framework.response import Response from rest_framework import status from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt from oauth2_provider.models import Application, AccessToken from oauth2_provider.settings import oauth2_settings from django.utils.crypto import generate_token from datetime import datetime, timedelta from .serializers import UserSerializer from django.contrib.auth.models import User from .models import Driver, Customer @permission_classes((AllowAny,)) @csrf_exempt def user_sign_up(request): if request.method == "POST": # user data serializer = UserSerializer(data=request.POST) # typed username username = request.POST.get('username') password = request.POST.get('password') first_name = request.POST.get('first_name') last_name = request.POST.get('last_name') # typed email email = request.POST.get('email') if User.objects.filter(username=username).exists(): return JsonResponse({"dublicated username": "your username already exists, try another username"}) elif User.objects.filter(email=email).exists(): return JsonResponse({"dublicated email": "your email already exists, try another email"}) else: # generating access token for each user expire_seconds = oauth2_settings.ACCESS_TOKEN_EXPIRE_SECONDS scopes = oauth2_settings.user_settings['SCOPES'] application = Application.objects.get(name="test_app_name") expires = datetime.now() + timedelta(seconds=expire_seconds) if serializer.is_valid(): user = User.objects.create_user(username, email, password) user.first_name = first_name user.last_name = last_name user.save() access_token = AccessToken.objects.create(user=User.objects.get(username=username), application=application, token=generate_token(), expires=expires, scope=scopes) user_type = request.POST.get('user_type') if user_type == "driver": driver = Driver.objects.get_or_create( user_id=User.objects.get(username=username).id) elif user_type == "customer": customer = Customer.objects.get_or_create( user_id=User.objects.get(username=username).id) # user_data = [] # user_data.append(serializer.data) # user_data.append(access_token.token) return JsonResponse({"registered_user": serializer.data, "access_token": access_token.token}) else: return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
urls.py
path('user/sign_up/', apis.user_sign_up), # 生产环境触发500错误,本地正常
DigitalOcean日志

排查结果与修复方案
从日志截图可见核心错误:Application matching query does not exist,即生产环境数据库中缺少代码里依赖的名为test_app_name的OAuth2应用实例,本地环境因提前创建过该记录所以正常运行。
具体修复步骤:
在生产环境创建OAuth2 Application记录
登录生产环境应用容器,运行Django shell执行以下代码:from oauth2_provider.models import Application from django.contrib.auth.models import User # 获取超级用户作为应用所有者(确保生产环境已创建超级用户) admin_user = User.objects.get(is_superuser=True) # 创建指定名称的OAuth2应用 Application.objects.create( name="test_app_name", user=admin_user, client_type=Application.CLIENT_CONFIDENTIAL, authorization_grant_type=Application.GRANT_PASSWORD, redirect_uri="https://akalat-shopcsphu.ondigitalocean.app" # 替换为实际生产域名 )优化代码异常处理
在apis.py的user_sign_up函数中增加异常捕获,避免直接抛出500错误:from django.core.exceptions import ObjectDoesNotExist # ... 原有代码 ... try: application = Application.objects.get(name="test_app_name") except ObjectDoesNotExist: return JsonResponse( {"error": "系统OAuth2应用未配置"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR ) # ... 后续代码 ...修正ALLOWED_HOSTS配置
ALLOWED_HOSTS不应包含https://前缀,修改settings.py中的配置:ALLOWED_HOSTS = os.getenv( "DJANGO_ALLOWED_HOSTS", "www.aklalatshop.com,akalat-shopcsphu.ondigitalocean.app" ).split(",")确认数据库迁移完成
在生产环境执行数据库迁移,确保oauth2_provider的表已正确创建:python manage.py migrate
内容的提问来源于stack exchange,提问作者abdallah eslah
相关产品推荐
相关产品推荐

