简体   繁体   中英

Django API OneToOne relationship using Auth, User model, Views, Serializers

I'm trying to connect the HighScore model to my User model. This way I can save a HighScore instance for each User. No matter what I do, I get a 500 internal server error.

I've tried creating a custom user model using AbstractUser. I've tried setting up OneToOne using settings.AUTH_USER_MODEL, and I've tried doing User = get_user_model() and all come back with a 500 internal server error.

# models.py

from django.conf import settings
from django.db import models

# Create your models here.
class HighScore(models.Model):
    # user = models.OneToOneField(
    #     settings.AUTH_USER_MODEL,
    #     on_delete=models.CASCADE,
    #     primary_key=True,
    # )
    value = models.IntegerField(default=0)

    def __str__(self):
        return "{}".format(self.value)
# urls.py

from django.urls import path
from .views import ListHighScoresView, CreateHighScoresView, HighScoresDetailView, LoginView, RegisterUsersView

urlpatterns = [
    path("highscores/", ListHighScoresView.as_view(), name="high-scores-all"),
    path("highscores/create/", CreateHighScoresView.as_view(), name="high-scores-create"),
    path("highscores/<int:pk>/", HighScoresDetailView.as_view(), name="high-scores-detail"),
    path("auth/login/", LoginView.as_view(), name="auth-login"),
    path("auth/register/", RegisterUsersView.as_view(), name="auth-register"),
]
# serializers.py

from rest_framework import serializers
from .models import HighScore
from django.contrib.auth.models import User

class HighScoreSerializer(serializers.ModelSerializer):
    class Meta:
        model = HighScore
        fields = ("id", "value")

        def update(self, instance, validated_data):
            instance.value = validated_data.get("value", instance.value)
            # instance.user = validated_data.get("user", instance.user)
            instance.save()
            return instance

class TokenSerializer(serializers.Serializer):
    token = serializers.CharField(max_length=255)

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ("username", "email")
# migration 0001_initial.py

# Generated by Django 2.0.3 on 2019-09-11 07:55

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

    initial = True

    dependencies = [
        ('auth', '0009_alter_user_last_name_max_length'),
    ]

    operations = [
        migrations.CreateModel(
            name='HighScore',
            fields=[
                ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)),
                ('value', models.IntegerField(default=0)),
            ],
        ),
    ]
# views.py

from django.shortcuts import render

from django.contrib.auth.models import User
from django.contrib.auth import authenticate, login

from rest_framework_jwt.settings import api_settings
from rest_framework.response import Response
from rest_framework.generics import GenericAPIView, RetrieveUpdateDestroyAPIView, ListAPIView
from rest_framework.views import APIView
from rest_framework import permissions, status

from .decorators import validate_request_data
from .models import HighScore
from .serializers import HighScoreSerializer, TokenSerializer, UserSerializer

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER

class CreateHighScoresView(GenericAPIView):
    """ GET and POST highscores/ """
    queryset = HighScore.objects.all()
    serializer_class = HighScoreSerializer
    permission_classes = (permissions.IsAuthenticated,)

    @validate_request_data
    def post(self, request, *args, **kwargs):
        new_high_score = HighScore.objects.create(
            # user=request.user,
            value=request.data["value"],
        )
        return Response(
            data=HighScoreSerializer(new_high_score).data,
            status=status.HTTP_201_CREATED
        )

class HighScoresDetailView(RetrieveUpdateDestroyAPIView):
    """ GET, PUT, DELETE highscores/:id/ """
    queryset = HighScore.objects.all()
    serializer_class = HighScoreSerializer

    def get(self, request, *args, **kwargs):
        try:
            new_high_score = self.queryset.get(pk=kwargs["pk"])
            return Response(HighScoreSerializer(new_high_score).data)
        except HighScore.DoesNotExist:
            return Response(
                data={
                    "message": "Yo, that High Score with id: {} does not exist".format(kwargs["pk"])
                },
                status=status.HTTP_404_NOT_FOUND
        )

    @validate_request_data
    def put(self, request, *args, **kwargs):
        try:
            new_high_score = self.queryset.get(pk=kwargs["pk"])
            serializer = HighScoreSerializer()
            updated_high_score = serializer.update(new_high_score, request.data)
            return Response(HighScoreSerializer(updated_high_score).data)
        except HighScore.DoesNotExist:
            return Response(
                data={
                    "message": "Yo, that High Score with id: {} does not exist".format(kwargs["pk"])
                },
                status=status.HTTP_404_NOT_FOUND
            )

    def delete(self, request, *args, **kwargs):
        try:
            new_high_score = self.queryset.get(pk=kwargs["pk"])
            new_high_score.delete()
            return Response(status=status.HTTP_204_NO_CONTENT)
        except HighScore.DoesNotExist:
            return Response(
                data={
                    "message": "Yo, that High Score with id: {} does not exist".format(kwargs["pk"])
                },
                status=status.HTTP_404_NOT_FOUND
            )

class ListHighScoresView(ListAPIView):
    """ Provides a GET method handler. """
    queryset = HighScore.objects.all()
    serializer_class = HighScoreSerializer
    permission_classes = (permissions.IsAuthenticated,)

class LoginView(APIView):
    """ POST auth/login/ """
    # This permission class will overide the global permission class setting
    permission_classes = (permissions.AllowAny,)

    queryset = User.objects.all()

    def post(self, request, *args, **kwargs):
        username = request.data.get("username", "")
        password = request.data.get("password", "")
        user = authenticate(request, username=username, password=password)
        if user is not None:
            # Login saves the user's ID in the session, using Django's session framework.
            login(request, user)
            serializer = TokenSerializer(
                data={
                    # Using drf jwt utility functions to generate a token
                    "token": jwt_encode_handler(
                        jwt_payload_handler(user)
                    )
                }
            )
            serializer.is_valid()
            username = user.username
            email = user.email
            token = serializer.data["token"]
            return Response(
                data={
                    "username": username,
                    "email": email,
                    "token": token
                },
                status=status.HTTP_201_CREATED
            )
        return Response(status=status.HTTP_401_UNAUTHORIZED)

class RegisterUsersView(APIView):
    """ Post auth/register/ """
    permission_classes = (permissions.AllowAny,)

    def post(self, request, *args, **kwargs):
        username = request.data.get("username", "")
        password = request.data.get("password", "")
        email = request.data.get("email", "")
        if not username and not password and not email:
            return Response(
                data={
                    "message": "Yo, gotta have a username, password, and email to register."
                },
                status=status.HTTP_400_BAD_REQUEST
            )
        new_user = User.objects.create_user(
            username=username, password=password, email=email
        )
        user = authenticate(request, username=username, password=password)
        login(request, user)
        serializer = TokenSerializer(
            data={
                # Using drf jwt utility functions to generate a token.
                "token": jwt_encode_handler(
                    jwt_payload_handler(user)
                )
            }
        )
        serializer.is_valid()
        token = serializer.data["token"]
        return Response(
            data={
                "username": username,
                "email": email,
                "token": token
            },
            status=status.HTTP_201_CREATED
        )
#settings.py

import os
import datetime
import django_heroku
from django.conf import settings
from django.http import HttpResponseRedirect

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

DEBUG = False

ALLOWED_HOSTS = [
    '127.0.0.1',
    '.herokuapp.com'
]

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'corsheaders',
    'rest_framework',
    'edjudicator_game',
]

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',
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
]

SECURE_SSL_REDIRECT = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True

ROOT_URLCONF = 'edjudicator_api.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',
            ],
        },
    },
]

WSGI_APPLICATION = 'edjudicator_api.wsgi.application'

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'c',
        'USER': 'a',
        'PASSWORD': 'b',
        'HOST': 'localhost',
        'PORT': '5432',
    },
}

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',
    },
]

LOGIN_REDIRECT_URL = '/'

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'America/Denver'

USE_I18N = True

USE_L10N = True

USE_TZ = True

PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')

REST_FRAMEWORK = {
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly',
    ],
}

JWT_AUTH = {
    'JWT_ENCODE_HANDLER':
    'rest_framework_jwt.utils.jwt_encode_handler',

    'JWT_DECODE_HANDLER':
    'rest_framework_jwt.utils.jwt_decode_handler',

    'JWT_PAYLOAD_HANDLER':
    'rest_framework_jwt.utils.jwt_payload_handler',

    'JWT_PAYLOAD_GET_USER_ID_HANDLER':
    'rest_framework_jwt.utils.jwt_get_user_id_from_payload_handler',

    'JWT_RESPONSE_PAYLOAD_HANDLER':
    'rest_framework_jwt.utils.jwt_response_payload_handler',

    'JWT_SECRET_KEY': SECRET_KEY,
    'JWT_GET_USER_SECRET_KEY': None,
    'JWT_PUBLIC_KEY': None,
    'JWT_PRIVATE_KEY': None,
    'JWT_ALGORITHM': 'HS256',
    'JWT_VERIFY': True,
    'JWT_VERIFY_EXPIRATION': True,
    'JWT_LEEWAY': 0,
    'JWT_EXPIRATION_DELTA': datetime.timedelta(hours=4),
    'JWT_AUDIENCE': None,
    'JWT_ISSUER': None,

    'JWT_ALLOW_REFRESH': False,
    'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),

    'JWT_AUTH_HEADER_PREFIX': 'Bearer',
    'JWT_AUTH_COOKIE': None,
}

django_heroku.settings(locals())

class SSLMiddleware(object):

    def process_request(self, request):
        if not any([settings.DEBUG, request.is_secure(), request.META.get("HTTP_X_FORWARDED_PROTO", "") == 'https']):
            url = request.build_absolute_uri(request.get_full_path())
            secure_url = url.replace("http://", "https://")
            return HttpResponseRedirect(secure_url)
# error stack

2019-09-11T07:52:36.173427+00:00 heroku[router]: at=info method=POST path="/api/v1/auth/login/" host=edjudicatorback.herokuapp.com request_id=dcdade1d-4581-4914-88bb-a90d40bce845 fwd="4.34.47.42" dyno=web.1 connect=1ms service=436ms status=201 bytes=883 protocol=https
2019-09-11T07:52:36.176354+00:00 app[web.1]: 10.143.94.115 - - [11/Sep/2019:01:52:36 -0600] "POST /api/v1/auth/login/ HTTP/1.1" 201 257 "https://edjudicator.herokuapp.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36"
2019-09-11T07:52:42.632366+00:00 heroku[router]: at=info method=GET path="/api/v1/highscores/" host=edjudicatorback.herokuapp.com request_id=2ae69eaf-3a2f-4818-a811-3bfcd4a6029b fwd="4.34.47.42" dyno=web.1 connect=1ms service=123ms status=500 bytes=18715 protocol=https
2019-09-11T07:52:42.526114+00:00 app[web.1]: Internal Server Error: /api/v1/highscores/
2019-09-11T07:52:42.526128+00:00 app[web.1]: Traceback (most recent call last):
2019-09-11T07:52:42.526139+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/db/backends/utils.py", line 85, in _execute
2019-09-11T07:52:42.526142+00:00 app[web.1]: return self.cursor.execute(sql, params)
2019-09-11T07:52:42.526144+00:00 app[web.1]: psycopg2.errors.UndefinedColumn: column edjudicator_game_highscore.user_id does not exist
2019-09-11T07:52:42.526148+00:00 app[web.1]: LINE 1: SELECT "edjudicator_game_highscore"."user_id", "edjudicator_...
2019-09-11T07:52:42.526151+00:00 app[web.1]: ^
2019-09-11T07:52:42.526153+00:00 app[web.1]:
2019-09-11T07:52:42.526155+00:00 app[web.1]:
2019-09-11T07:52:42.526161+00:00 app[web.1]: The above exception was the direct cause of the following exception:
2019-09-11T07:52:42.526163+00:00 app[web.1]:
2019-09-11T07:52:42.526166+00:00 app[web.1]: Traceback (most recent call last):
2019-09-11T07:52:42.526168+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/core/handlers/exception.py", line 35, in inner
2019-09-11T07:52:42.526170+00:00 app[web.1]: response = get_response(request)
2019-09-11T07:52:42.526178+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/core/handlers/base.py", line 128, in _get_response
2019-09-11T07:52:42.526180+00:00 app[web.1]: response = self.process_exception_by_middleware(e, request)
2019-09-11T07:52:42.526183+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/core/handlers/base.py", line 126, in _get_response
2019-09-11T07:52:42.526185+00:00 app[web.1]: response = wrapped_callback(request, *callback_args, **callback_kwargs)
2019-09-11T07:52:42.526187+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
2019-09-11T07:52:42.526190+00:00 app[web.1]: return view_func(*args, **kwargs)
2019-09-11T07:52:42.526192+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/views/generic/base.py", line 69, in view
2019-09-11T07:52:42.526194+00:00 app[web.1]: return self.dispatch(request, *args, **kwargs)
2019-09-11T07:52:42.526197+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/rest_framework/views.py", line 505, in dispatch
2019-09-11T07:52:42.526199+00:00 app[web.1]: response = self.handle_exception(exc)
2019-09-11T07:52:42.526201+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/rest_framework/views.py", line 465, in handle_exception
2019-09-11T07:52:42.526203+00:00 app[web.1]: self.raise_uncaught_exception(exc)
2019-09-11T07:52:42.526205+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/rest_framework/views.py", line 476, in raise_uncaught_exception
2019-09-11T07:52:42.526207+00:00 app[web.1]: raise exc
2019-09-11T07:52:42.526210+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/rest_framework/views.py", line 502, in dispatch
2019-09-11T07:52:42.526212+00:00 app[web.1]: response = handler(request, *args, **kwargs)
2019-09-11T07:52:42.526214+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/rest_framework/generics.py", line 199, in get
2019-09-11T07:52:42.526216+00:00 app[web.1]: return self.list(request, *args, **kwargs)
2019-09-11T07:52:42.526217+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/rest_framework/mixins.py", line 46, in list
2019-09-11T07:52:42.526219+00:00 app[web.1]: return Response(serializer.data)
2019-09-11T07:52:42.526220+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/rest_framework/serializers.py", line 757, in data
2019-09-11T07:52:42.526222+00:00 app[web.1]: ret = super().data
2019-09-11T07:52:42.526224+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/rest_framework/serializers.py", line 261, in data
2019-09-11T07:52:42.526226+00:00 app[web.1]: self._data = self.to_representation(self.instance)
2019-09-11T07:52:42.526227+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/rest_framework/serializers.py", line 675, in to_representation
2019-09-11T07:52:42.526229+00:00 app[web.1]: self.child.to_representation(item) for item in iterable
2019-09-11T07:52:42.526231+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/db/models/query.py", line 272, in __iter__
2019-09-11T07:52:42.526232+00:00 app[web.1]: self._fetch_all()
2019-09-11T07:52:42.526234+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/db/models/query.py", line 1179, in _fetch_all
2019-09-11T07:52:42.526235+00:00 app[web.1]: self._result_cache = list(self._iterable_class(self))
2019-09-11T07:52:42.526237+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/db/models/query.py", line 53, in __iter__
2019-09-11T07:52:42.526239+00:00 app[web.1]: results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
2019-09-11T07:52:42.526240+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 1066, in execute_sql
2019-09-11T07:52:42.526242+00:00 app[web.1]: cursor.execute(sql, params)
2019-09-11T07:52:42.526243+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/db/backends/utils.py", line 100, in execute
2019-09-11T07:52:42.526245+00:00 app[web.1]: return super().execute(sql, params)
2019-09-11T07:52:42.526246+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/db/backends/utils.py", line 68, in execute
2019-09-11T07:52:42.526248+00:00 app[web.1]: return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
2019-09-11T07:52:42.526250+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/db/backends/utils.py", line 77, in _execute_with_wrappers
2019-09-11T07:52:42.526251+00:00 app[web.1]: return executor(sql, params, many, context)
2019-09-11T07:52:42.526253+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/db/backends/utils.py", line 85, in _execute
2019-09-11T07:52:42.526254+00:00 app[web.1]: return self.cursor.execute(sql, params)
2019-09-11T07:52:42.526256+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/db/utils.py", line 89, in __exit__
2019-09-11T07:52:42.526265+00:00 app[web.1]: raise dj_exc_value.with_traceback(traceback) from exc_value
2019-09-11T07:52:42.526267+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/db/backends/utils.py", line 85, in _execute
2019-09-11T07:52:42.526268+00:00 app[web.1]: return self.cursor.execute(sql, params)
2019-09-11T07:52:42.526270+00:00 app[web.1]: django.db.utils.ProgrammingError: column edjudicator_game_highscore.user_id does not exist
2019-09-11T07:52:42.526271+00:00 app[web.1]: LINE 1: SELECT "edjudicator_game_highscore"."user_id", "edjudicator_...
2019-09-11T07:52:42.526273+00:00 app[web.1]: ^
2019-09-11T07:52:42.526325+00:00 app[web.1]:
2019-09-11T07:52:42.633754+00:00 app[web.1]: 10.143.94.115 - - [11/Sep/2019:01:52:42 -0600] "GET /api/v1/highscores/ HTTP/1.1" 500 18385 "https://edjudicator.herokuapp.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36"

All I'm trying to do is have the HighScore model have a OneToOne relationship with the User. That way when the User logs in I can send the POST request from my frontend to create a HighScore for that User. Any and all help will be sooo much appreciated!!!

EDIT: I needed to reset my database. Dropped my psql database using drop database <database name> then create database <db name> with owner <user_name> also did heroku restart plus heroku pg:reset DATABASE and ran migrations on both to get them working. Thank you all!

You don't need to create a frontend for this, you can simply use a post save signal to do it:

class HighScore(models.Model):
    user = models.OneToOneField(
         settings.AUTH_USER_MODEL,
         on_delete=models.CASCADE,
         primary_key=True,
    )
    value = models.IntegerField(default=0)

@receiver(post_save, sender=User)
def create_user_score(sender, instance, created, **kwargs):
    if created:
        HighScore.objects.create(user=instance)

The error tells you what is going on: you simply haven't added the new column to the database.

You need to migrate. Run makemigrations on your local machine, commit the result, push to Heroku, then run migrate on Heroku.

Note, you really shouldn't be developing directly on Heroku anyway. Run the dev server locally.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM