Ошибка атрибута фреймворка Django Rest: ошибка атрибута при попытке получить значение для поля

#python #django #django-rest-framework

#python #django #django-rest-framework

Вопрос:

Я пытался добавить атрибуты в пользовательский профиль, создав OneToOneField для пользователя и добавив разные поля. Теперь я запускаю это и вызываю api с приведенным ниже телом. API может быть успешно проанализирован. Пользователь создается в таблице пользователей и таблице профилей пользователей с правильными атрибутами. Однако Django возвращает ошибку AttributeError: Got AttributeError when attempting to get a value for field first_name on serializer UserProfileSerializer . Это имеет смысл, поскольку модель не имеет этих атрибутов, но каков правильный способ передать json таким же образом и создать пользователя в User таблице и UserProfile таблице?

 {
    "first_name": "Jay",
    "last_name" : "Patel",
    "email": "tes1t@email.com",
    "password": "password",
    "tier": "Gold",
    "bkms_id": "12234"
}
 

model.py

 # Create your models here.
class UserProfile(models.Model):
    MedalType: List[Tuple[str, str]] = [('Bronze', 'Bronze'), ('Silver', 'Silver'), ('Gold', 'Gold')]
    bkms_id = models.PositiveIntegerField()
    tier = models.CharField(choices=MedalType, max_length=100)
    user = models.OneToOneField(to=User, on_delete=models.CASCADE)

    def __str__(self):
        return self.user.username
 

serializer.py

 from typing import Dict

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

from authentication.models import UserProfile


class UserProfileSerializer(serializers.ModelSerializer):
    password = serializers.CharField(max_length=65, min_length=8, write_only=True, required=True,
                                     style={'input_type': 'password'})
    email = serializers.EmailField(max_length=255, min_length=4, required=True)
    first_name = serializers.CharField(max_length=255, min_length=2, required=True)
    last_name = serializers.CharField(max_length=255, min_length=2, required=True)
    bkms_id = serializers.IntegerField(required=True, min_value=0)
    tier = serializers.CharField(required=True)

    class Meta:
        model = UserProfile
        fields = ['first_name', 'last_name', 'email', 'password', 'bkms_id', 'tier']

    def validate(self, attrs):
        if User.objects.filter(email=attrs.get('email', '')).exists():
            raise serializers.ValidationError({'email': 'Email is already in use'})
        if UserProfile.objects.filter(bkms_id=attrs.get('bkms_id', '')).exists():
            raise serializers.ValidationError({'bkms_id': 'BKMS is already in use'})
        return super().validate(attrs)

    def create(self, validated_data: Dict):
        email = validated_data.pop('email', '')
        password = validated_data.pop('password', '')
        first_name = validated_data.pop('first_name', '')
        last_name = validated_data.pop('last_name', '')
        user = User.objects.create_user(email, email, password, first_name=first_name, last_name=last_name)
        user.save()
        validated_data['user'] = user
        return UserProfile.objects.create(**validated_data)
 

views.py

 # Create your views here.
from rest_framework import status
from rest_framework.generics import GenericAPIView
from rest_framework.permissions import AllowAny
from rest_framework.response import Response

from authentication.serializers import UserProfileSerializer


class RegisterView(GenericAPIView):
    serializer_class = UserProfileSerializer
    permission_classes = [AllowAny]

    def post(self, request):
        serializer = UserProfileSerializer(data=request.data)

        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
 

Ответ №1:

Я бы предложил создать разные сериализаторы, подобные этому:

 class UserSerializer(serializers.ModelSerializer):
    password = serializers.CharField(max_length=65, min_length=8, write_only=True, required=True,
                                     style={'input_type': 'password'})
    email = serializers.EmailField(max_length=255, min_length=4, required=True)
    first_name = serializers.CharField(max_length=255, min_length=2, required=True)
    last_name = serializers.CharField(max_length=255, min_length=2, required=True)

    class Meta:
        model = User
        fields = ['first_name', 'last_name', 'email', 'password']

class UserProfileSerializer(serializers.ModelSerializer):
    user = UserSerializer()
    bkms_id = serializers.IntegerField(required=True, min_value=0)
    tier = serializers.CharField(required=True)

    class Meta:
        model = UserProfile
        fields = ['user', 'bkms_id', 'tier']

    def validate(self, attrs):
        if User.objects.filter(email=attrs.get('email', '')).exists():
            raise serializers.ValidationError({'email': 'Email is already in use'})
        if UserProfile.objects.filter(bkms_id=attrs.get('bkms_id', '')).exists():
            raise serializers.ValidationError({'bkms_id': 'BKMS is already in use'})
        attrs['user'] = {
           'email': attrs.get('email', ''),
           'first_name': attrs.get('first_name', ''),
           'last_name': attrs.get('last_name', ''),
           'password': attrs.get('password', '')
        }
        return super().validate(attrs)

    def create(self, validated_data: Dict):
       validated_user = validated_data.pop('user', {})
       email = validated_user.pop('email', '')
       password = validated_user.pop('password', '')
       first_name = validated_user.pop('first_name', '')
       last_name = validated_user.pop('last_name', '')
       user = User.objects.create_user(email=email, password=password, first_name=first_name, last_name=last_name)
       user.save()
       validated_data['user'] = user
       return UserProfile.objects.create(**validated_data)