python django {' user':[errordetail(string ='不正确类型。期望的pk value,接收到boundfield。

发布于 2025-02-03 17:25:17 字数 8546 浏览 2 评论 0原文

我正在Django上发展后端。我在注册视图类中编写了用于用户注册的代码,以前效果很好。我不得不将电子邮件确认合并到其中,这也可以单独使用。但是,完成所有操作后,我添加了一些条件以确保工作正常,但是我得到了“ string ='不正确的类型。预期的pk value,接收到了boundfield。”和“ forbidden:/pibi/v1.0/user /register-patient/“错误。

另外,要澄清工作流程。注册时,每个用户都会为JWT令牌分配。该令牌与发送给电子邮件的代币不同。我将后者命名为“ verification_token”。

因此,用户签名,他们的“ IS_ACTIVE”字段设置为false,直到他们单击电子邮件中收到的链接为止。 SendGrid电子邮件API用于发送电子邮件。仅当DEBERE_SERIALIZER数据存储在数据库中时,该电子邮件才会发送。

当用户单击通过电子邮件接收到的链接时,他们的“ IS_ACTIVE”字段将设置为true,现在可以登录。

此外,实际的SendGrid API密钥是代码的一部分,我在此处将其删除。

我的models.py文件:


from django.db import models
from django.contrib.auth.models import User

class VerificatonToken(models.Model):
    user = models.ForeignKey(
        User, on_delete=models.CASCADE, related_name='token')
    verification_token = models.CharField(
        max_length=256, blank=True, null=True)

我的serializer.py文件:

from rest_framework import serializers
from django.contrib.auth.models import User
from rest_framework.validators import UniqueValidator
from rest_framework_jwt.settings import api_settings
from .models import VerificatonToken


class TokenSerializer(serializers.ModelSerializer):
    class Meta:
        model = VerificatonToken
        fields = ('user', 'verification_token',)


class PatientSerializer(serializers.ModelSerializer):

    token = serializers.SerializerMethodField()

    email = serializers.EmailField(
        required=True,
        validators=[UniqueValidator(queryset=User.objects.all())]
    )

    username = serializers.CharField(
        required=True,
        max_length=32,
        validators=[UniqueValidator(queryset=User.objects.all())]
    )

    first_name = serializers.CharField(
        required=True,
        max_length=32
    )

    last_name = serializers.CharField(
        required=True,
        max_length=32
    )

    # DOB = serializers.DateField(
    #     required=True
    # )

    # gender = serializers.CharField(
    #     required=True
    # )

    # address = serializers.CharField(
    #     required=True,
    #     max_length=60
    # )

    # contactNo = serializers.IntegerField(
    #     required=True,
    #     max_length=11
    # )

    password = serializers.CharField(
        required=True,
        min_length=8,
        write_only=True
    )

    def create(self, validated_data):
        password = validated_data.pop('password', None)
        instance = self.Meta.model(**validated_data)
        if password is not None:
            instance.set_password(password)
        instance.save()
        return instance

    def get_token(self, obj):
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
        payload = jwt_payload_handler(obj)
        token = jwt_encode_handler(payload)
        return token

    class Meta:
        model = User
        fields = (
            'token',
            'username',
            'password',
            'first_name',
            'last_name',
            'email',
            'is_active',
            # 'DOB',
            # 'address',
            # 'contactNo',
            'id'
        )

我的views.py文件:

from rest_framework.views import APIView
from rest_framework.response import Response
from .serializers import PatientSerializer, TokenSerializer
from django.contrib.auth.models import User
from django.contrib.auth import authenticate
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail
import uuid
import hashlib
from .models import VerificatonToken
from rest_framework.permissions import AllowAny



class TestView(APIView):
    def get(self, request, format=None):
        print("API called")
        return Response("You did it!", status=200)


# Registering User & Sending Email Confirmation

class RegisterView(APIView):

    permission_classes = [AllowAny]

    def post(self, request, format=None):
        print("Registering User")

        user_data = request.data
        user_data['is_active'] = False
        print(request.data)

        patient_serializer = PatientSerializer(data=user_data)

        if patient_serializer.is_valid(raise_exception=False):
            patient_serializer.save()

            salt = uuid.uuid4().hex
            hash_object = hashlib.sha256(
                salt.encode() + str(patient_serializer.data['id']).encode())
            verification_token = hash_object.hexdigest() + ':' + salt

            verification_token_serializer = TokenSerializer(
                data={'user': patient_serializer['id'], 'token': verification_token})

            if verification_token_serializer.is_valid(raise_exception=False):
                verification_token_serializer.save()

                message = Mail(
                    from_email='[email protected]',
                    to_emails=user_data['email'],
                    subject='Please Confirm Your Email Address',
                    html_content=f"Hi {user_data['username']}!\
                        <br><br>\
                        Welcome to All-in-one Health App. To finish signing up, please click \
                        <a href='http://localhost:8000/api/v1.0/user/verify-patient/{verification_token}'>HERE</a>\
                ")

                try:

                    sg = SendGridAPIClient(
                        '<Sendgrid API KEY>')
                    response = sg.send(message)
                    print(response)
                    return Response(patient_serializer.data, status=200)

                except Exception as e:
                    print('ERROR', e)

            else:
                print(patient_serializer.errors,
                      verification_token_serializer.errors)

        return Response({'msg': 'error'}, status=403)


# RegisterPatientView() provides customized link to VerifyPatientView() in email to patient
# compares the token provided to verify the email

class PatientVerificationView(APIView):

    def get(self, request, pk, format=None):
        print("Verifying Patient", pk)

        tokenObj = VerificatonToken.objects.filter(token=pk).first()

        user = User.objects.filter(id=tokenObj.user.id).first()
        if user:
            patient_serializer = PatientSerializer(
                user, data={'is_active': True})
            if patient_serializer.is_valid(raise_exception=False):
                patient_serializer.save()
                return Response('Patient email verified', status=200)

        return Response(status=404)


class PatientLoginView(APIView):

    # convert user token to user data

    def get(self, request, format=None):

        if request.user.is_authenticated == False or request.user.is_active == False:
            return Response('Invalid credentials', status=403)

        user = PatientSerializer(request.user)
        print(user.data)

        return Response("Testing", status=200)

    # authenticate user data

    def post(self, request, format=None):
        print("Login class")

        # Looking for user's email or username
        user_obj = User.objects.filter(email=request.data['username']).first(
        ) or User.objects.filter(username=request.data['username']).first()

        # if user is valid, returns user data in credentials
        if user_obj is not None:
            credentials = {
                'username': user_obj.username,
                'password': request.data['password']
            }
            user = authenticate(**credentials)

        # if user is valid and active, logs user in
            if user and user.is_active:
                user_serializer = PatientSerializer(user)
                print("Login successful")
                return Response(user_serializer.data, status=200)

        return Response("Invalid credentials", status=403)

邮递员请求:

“

命令行:

“在此处输入图像说明”

Tokenserializer基本上是给出错误的。 patiet_serializer工作正常,因为用户信息将“ In_Active”字段设置为false进入数据库。我不知道如何修复它。

I'm developing my backend on Django. I wrote a code for user registration under RegistrationView class, and it worked fine before. I had to incorporate email confirmation in it, which also worked fine on its own. However, after all of that was done, I added some conditions to ensure proper working, but I get the "string='Incorrect type. Expected pk value, received BoundField.'" and "Forbidden: /api/v1.0/user/register-patient/" error.

Also, to clarify the workflow. Each user is assigned a JWT token while signing up. This token is different from the token sent to verify email. I named the latter "verification_token".

So a user signs up, their 'is_active' field is set to False until they click on the link received in their email. Sendgrid email API was used to send the email. The email is only sent if the patient_serializer data is stored in the db.

When the user clicks on the link received through email, their 'is_active' field is set to True, and they can now log in.

Also, the actual sendgrid API key is a part of the code, I removed it here.

My models.py file:


from django.db import models
from django.contrib.auth.models import User

class VerificatonToken(models.Model):
    user = models.ForeignKey(
        User, on_delete=models.CASCADE, related_name='token')
    verification_token = models.CharField(
        max_length=256, blank=True, null=True)

My serializers.py file:

from rest_framework import serializers
from django.contrib.auth.models import User
from rest_framework.validators import UniqueValidator
from rest_framework_jwt.settings import api_settings
from .models import VerificatonToken


class TokenSerializer(serializers.ModelSerializer):
    class Meta:
        model = VerificatonToken
        fields = ('user', 'verification_token',)


class PatientSerializer(serializers.ModelSerializer):

    token = serializers.SerializerMethodField()

    email = serializers.EmailField(
        required=True,
        validators=[UniqueValidator(queryset=User.objects.all())]
    )

    username = serializers.CharField(
        required=True,
        max_length=32,
        validators=[UniqueValidator(queryset=User.objects.all())]
    )

    first_name = serializers.CharField(
        required=True,
        max_length=32
    )

    last_name = serializers.CharField(
        required=True,
        max_length=32
    )

    # DOB = serializers.DateField(
    #     required=True
    # )

    # gender = serializers.CharField(
    #     required=True
    # )

    # address = serializers.CharField(
    #     required=True,
    #     max_length=60
    # )

    # contactNo = serializers.IntegerField(
    #     required=True,
    #     max_length=11
    # )

    password = serializers.CharField(
        required=True,
        min_length=8,
        write_only=True
    )

    def create(self, validated_data):
        password = validated_data.pop('password', None)
        instance = self.Meta.model(**validated_data)
        if password is not None:
            instance.set_password(password)
        instance.save()
        return instance

    def get_token(self, obj):
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
        payload = jwt_payload_handler(obj)
        token = jwt_encode_handler(payload)
        return token

    class Meta:
        model = User
        fields = (
            'token',
            'username',
            'password',
            'first_name',
            'last_name',
            'email',
            'is_active',
            # 'DOB',
            # 'address',
            # 'contactNo',
            'id'
        )

My views.py file:

from rest_framework.views import APIView
from rest_framework.response import Response
from .serializers import PatientSerializer, TokenSerializer
from django.contrib.auth.models import User
from django.contrib.auth import authenticate
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail
import uuid
import hashlib
from .models import VerificatonToken
from rest_framework.permissions import AllowAny



class TestView(APIView):
    def get(self, request, format=None):
        print("API called")
        return Response("You did it!", status=200)


# Registering User & Sending Email Confirmation

class RegisterView(APIView):

    permission_classes = [AllowAny]

    def post(self, request, format=None):
        print("Registering User")

        user_data = request.data
        user_data['is_active'] = False
        print(request.data)

        patient_serializer = PatientSerializer(data=user_data)

        if patient_serializer.is_valid(raise_exception=False):
            patient_serializer.save()

            salt = uuid.uuid4().hex
            hash_object = hashlib.sha256(
                salt.encode() + str(patient_serializer.data['id']).encode())
            verification_token = hash_object.hexdigest() + ':' + salt

            verification_token_serializer = TokenSerializer(
                data={'user': patient_serializer['id'], 'token': verification_token})

            if verification_token_serializer.is_valid(raise_exception=False):
                verification_token_serializer.save()

                message = Mail(
                    from_email='[email protected]',
                    to_emails=user_data['email'],
                    subject='Please Confirm Your Email Address',
                    html_content=f"Hi {user_data['username']}!\
                        <br><br>\
                        Welcome to All-in-one Health App. To finish signing up, please click \
                        <a href='http://localhost:8000/api/v1.0/user/verify-patient/{verification_token}'>HERE</a>\
                ")

                try:

                    sg = SendGridAPIClient(
                        '<Sendgrid API KEY>')
                    response = sg.send(message)
                    print(response)
                    return Response(patient_serializer.data, status=200)

                except Exception as e:
                    print('ERROR', e)

            else:
                print(patient_serializer.errors,
                      verification_token_serializer.errors)

        return Response({'msg': 'error'}, status=403)


# RegisterPatientView() provides customized link to VerifyPatientView() in email to patient
# compares the token provided to verify the email

class PatientVerificationView(APIView):

    def get(self, request, pk, format=None):
        print("Verifying Patient", pk)

        tokenObj = VerificatonToken.objects.filter(token=pk).first()

        user = User.objects.filter(id=tokenObj.user.id).first()
        if user:
            patient_serializer = PatientSerializer(
                user, data={'is_active': True})
            if patient_serializer.is_valid(raise_exception=False):
                patient_serializer.save()
                return Response('Patient email verified', status=200)

        return Response(status=404)


class PatientLoginView(APIView):

    # convert user token to user data

    def get(self, request, format=None):

        if request.user.is_authenticated == False or request.user.is_active == False:
            return Response('Invalid credentials', status=403)

        user = PatientSerializer(request.user)
        print(user.data)

        return Response("Testing", status=200)

    # authenticate user data

    def post(self, request, format=None):
        print("Login class")

        # Looking for user's email or username
        user_obj = User.objects.filter(email=request.data['username']).first(
        ) or User.objects.filter(username=request.data['username']).first()

        # if user is valid, returns user data in credentials
        if user_obj is not None:
            credentials = {
                'username': user_obj.username,
                'password': request.data['password']
            }
            user = authenticate(**credentials)

        # if user is valid and active, logs user in
            if user and user.is_active:
                user_serializer = PatientSerializer(user)
                print("Login successful")
                return Response(user_serializer.data, status=200)

        return Response("Invalid credentials", status=403)

Postman request:

enter image description here

Command line:

enter image description here

EDIT: My TokenSerializer is basically the one giving an error. The Patiet_Serializer is working fine because the user info goes into the database with the 'in_active' field set to false. I can't figure out how to fix it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文