【问题标题】:Update user error with Django API rest framework使用 Django API 休息框架更新用户错误
【发布时间】:2017-11-01 18:56:40
【问题描述】:

我无法更新用户,因为 Django 在邮递员中给了我这个错误:

AttributeError at /profesionales/
Got AttributeError when attempting to get a value for field `user` on serializer `ProfesionalesSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `User` instance.
Original exception text was: 'User' object has no attribute 'user'.

Request Method: PUT
Request URL: http://127.0.0.1:8000/profesionales/
Django Version: 1.11.6
Python Executable: C:\Users\Ismael\AppData\Local\Programs\Python\Python36\python.exe
Python Version: 3.6.3

这是我的代码:

view.py

#Listar todos los profesionales o crear uno
#profesionales/
class ProfesionalesList(APIView):


    def get_object(self, pk):
        try:
            return User.objects.get(username=pk)
        except User.DoesNotExist:
            raise Http404

    def get(self, request ):
        usuarios = Profesionales.objects.all()
        usuarioSerializer = ProfesionalesSerializer(usuarios, many=True)
        return Response(usuarioSerializer.data)

    def post(self, request):
        profesionalSerializer = ProfesionalesSerializer(data=request.data)
        if profesionalSerializer.is_valid():
            profesionalSerializer.save()
            return Response(profesionalSerializer.data, status=status.HTTP_201_CREATED)
        else:
            return Response(profesionalSerializer.errors, status=status.HTTP_400_BAD_REQUEST)
    def put(self, request, *args, **kwargs):
        instance = self.get_object(request.data.get('user').get('username'))

        profesionalSerializer = ProfesionalesSerializer(instance, data=request.data)
        if profesionalSerializer.is_valid():
            profesionalSerializer.save()
            return Response(profesionalSerializer.data, status=status.HTTP_201_CREATED)
        else:
            return Response(profesionalSerializer.errors, status=status.HTTP_400_BAD_REQUEST)

serializers.py

class UserSerializer(serializers.Serializer):

    username = serializers.CharField()
    first_name = serializers.CharField(allow_blank=True)
    last_name = serializers.CharField(allow_blank=True)
    email = serializers.CharField(allow_blank=True)

    class Meta:
        fields = ('username', 'first_name', 'last_name', 'email')

        def create(self, validated_data):
            user = User.objects.create(**validated_data)
            return user

class ProfesionalesSerializer(serializers.Serializer):

    user = UserSerializer()
    numColegiado = serializers.CharField(allow_blank=False)

    class Meta:
            fields = ('user', 'numColegiado')

    def create(self, validated_data):
        user_data = validated_data.pop('user')
        user = User.objects.create(**user_data)
        profesional = Profesionales.objects.create(user=user, **validated_data)

        return profesional

    def update(self, instance, validated_data):

        num_colegiado = validated_data.get('numColegiado')
        user_data = validated_data.pop('user')
        user = User.objects.get(**user_data)

        profesionales = user.profesionales

        if num_colegiado:
            profesionales.numColegiado = num_colegiado
            profesionales.save()
        return instance

model.py

class Profesionales(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    dni = models.CharField(max_length=9, blank=True, default='')
    numColegiado = models.CharField(max_length=8, blank=True, default='')
    valoracionMedia = models.FloatField(blank=True, default=0)
    numVotos = models.IntegerField(blank=True, default=0)

    def __str__(self):
        return self.numColegiado

【问题讨论】:

    标签: python django rest django-rest-framework sql-update


    【解决方案1】:

    好的,我知道了 - 但老实说,您应该重新考虑您的 API 设计。

    问题是 - 没有问题 - 我的意思是从你的代码中我无法重现错误。我可能假设用户模型 - 如果你可以粘贴用户模型定义会很棒(或者如果这是一个标准的 django 用户 - 也提到这一点)。

    所以首先我会将序列化器更改为模型序列化器:

    serializers.py

    class UsernameValidator(object):
    
        def set_context(self, serializer_field):
            """
            This hook is called by the serializer instance,
            prior to the validation call being made.
            """
            # Determine the existing instance, if this is an update operation.
            self.instance = getattr(serializer_field.parent, 'instance', None)
            if not self.instance:
                # try to get user from profesionales:
                root_instance = getattr(serializer_field.root, 'instance', None)
                self.instance = getattr(root_instance, 'user', None)
    
        def __call__(self, value):
            if self.instance and User.objects.filter(username=value).exclude(id=self.instance.id).exists():
                raise ValidationError('Username already exists.')
    
            if not self.instance and User.objects.filter(username=value).exists():
                raise ValidationError('Username already exists.')
    
    
    class UserSerializer(serializers.ModelSerializer):
    
        username = serializers.CharField(max_length=128, validators=[UsernameValidator()])
    
        class Meta:
            model = User
            fields = ('username', 'first_name', 'last_name', 'email')
    
    
    class ProfesionalesSerializer(serializers.ModelSerializer):
    
        user = UserSerializer()
        numColegiado = serializers.CharField(allow_blank=False)
    
        class Meta:
            model = Profesionales
            fields = ('user', 'numColegiado')
    
        def create(self, validated_data):
            user_data = validated_data.pop('user')
            user = User.objects.create(**user_data)
            profesional = Profesionales.objects.create(user=user, **validated_data)
            return profesional
    
        def update(self, instance, validated_data):
            user_data = validated_data.pop('user')
            user = instance.user
            userSerializer = UserSerializer(user, data=user_data)
            if userSerializer.is_valid(raise_exception=True):
                userSerializer.save()
    
            num_colegiado = validated_data.get('numColegiado')
            if num_colegiado:
                instance.numColegiado = num_colegiado
                instance.save()
            return instance
    

    您可以注意到 - 我添加了 UsernameValidator,这对于 API 正常工作非常重要 0 - 它基本上搜索现有用户实例并检查用户名是否存在;

    我还更改了更新方法 - 现在它使用 UserSerializer 显式;还纠正了一些错误 - 返回 validated_data 而不是 instance 等等。

    最后views.py

    class ProfesionalesList(APIView):
    
        def get_object(self, pk):
            try:
                return User.objects.get(username=pk)
            except User.DoesNotExist:
                raise Http404
    
        def get(self, request ):
            usuarios = Profesionales.objects.all()
            usuarioSerializer = ProfesionalesSerializer(usuarios, many=True)
            return Response(usuarioSerializer.data)
    
        def post(self, request):
            profesionalSerializer = ProfesionalesSerializer(data=request.data)
            if profesionalSerializer.is_valid(raise_exception=True):
                profesionalSerializer.save()
            return Response(profesionalSerializer.data, status=status.HTTP_201_CREATED)
    
        def put(self, request, *args, **kwargs):
            user = self.get_object(request.data.get('user').get('username'))
            profesionalSerializer = ProfesionalesSerializer(user.profesionales, data=request.data)
            if profesionalSerializer.is_valid(raise_exception=True):
                profesionalSerializer.save()
            return Response(profesionalSerializer.data, status=status.HTTP_200_OK)
    

    我已经缩短了代码 - 在 is_valid 方法中使用 raise_exception

    实际上 - 很抱歉没有遵循 stackoverflow 规则 - 并且没有为您的实际问题提供答案 - 但我坚信分析示例您可以弄清楚。如果您还有其他问题 - 请询问。

    【讨论】:

      【解决方案2】:

      在您的班级ProfesionalesSerializer 中,您已将user 定义为UserSerializer 的一个实例。相反,user 应该是一个字段。您不能将序列化程序用作“字段”。

      编辑:忽略这个。原来你可以。见这里:http://www.django-rest-framework.org/api-guide/serializers/#dealing-with-nested-objects

      (感谢@opalczynski)

      【讨论】:

      • 对不起,我该怎么做? ,因为我需要将用户与模型用户链接。抱歉,但我是 django 的新手 :)。
      • 其实序列化器可以作为字段使用——它是嵌套序列化器——可以在drf官方文档中找到:django-rest-framework.org/api-guide/serializers/…
      • 稍后我会尝试找出真正的问题。
      猜你喜欢
      • 2015-02-25
      • 2019-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-11
      • 2022-01-11
      • 1970-01-01
      • 2013-04-08
      相关资源
      最近更新 更多