“用户”对象没有属性“配置文件”

时间:2019-04-02 14:38:26

标签: django

我已按照本教程https://thinkster.io/tutorials/django-json-api/profiles的要求为正在使用的应用程序创建了一个单独的用户配置文件应用程序,因此我希望配置文件与密码或用户名同时更新,因此我在配置文件/模型中创建了模型.py,然后将其导入身份验证模型。

根据本教程,它应该可以工作,但是我一直在收到此错误。让我分享我的 authentication / serializers.py

class LoginSerializer(serializers.Serializer):
    email = serializers.CharField(max_length=255)
    username = serializers.CharField(max_length=255, read_only=True)
    password = serializers.CharField(max_length=128, write_only=True)
    token = serializers.CharField(max_length=255, read_only=True)

    def validate(self, data):
        # The `validate` method is where we make sure that the current
        # instance of `LoginSerializer` has "valid". In the case of logging a
        # user in, this means validating that they've provided an email
        # and password and that this combination matches one of the users in
        # our database.
        email = data.get('email', None)
        password = data.get('password', None)

        # As mentioned above, an email is required. Raise an exception if an
        # email is not provided.
        if email is None:
            raise serializers.ValidationError(
                'An email address is required to log in.'
            )

        # As mentioned above, a password is required. Raise an exception if a
        # password is not provided.
        if password is None:
            raise serializers.ValidationError(
                'A password is required to log in.'
            )

        # The `authenticate` method is provided by Django and handles checking
        # for a user that matches this email/password combination. Notice how
        # we pass `email` as the `username` value. Remember that, in our User
        # model, we set `USERNAME_FIELD` as `email`.
        user = authenticate(username=email, password=password)

        # If no user was found matching this email/password combination then
        # `authenticate` will return `None`. Raise an exception in this case.
        if user is None:
            raise serializers.ValidationError(
                'A user with this email and password was not found.'
            )

        # Django provides a flag on our `User` model called `is_active`. The
        # purpose of this flag to tell us whether the user has been banned
        # or otherwise deactivated. This will almost never be the case, but
        # it is worth checking for. Raise an exception in this case.
        if not user.is_active:
            raise serializers.ValidationError(
                'This user has been deactivated.'
            )

        # The `validate` method should return a dictionary of validated data.
        # This is the data that is passed to the `create` and `update` methods
        # that we will see later on.
        return {
            'email': user.email,
            'username': user.username,
            'token': user.token
        }


class UserSerializer(serializers.ModelSerializer):
    """Handles serialization and deserialization of User objects."""

    # Passwords must be at least 8 characters, but no more than 128
    # characters. These values are the default provided by Django. We could
    # change them, but that would create extra work while introducing no real
    # benefit, so let's just stick with the defaults.
    password = serializers.CharField(
        max_length=128,
        min_length=8,
        write_only=True
    )
    # When a field should be handled as a serializer, we must explicitly say
    # so. Moreover, `UserSerializer` should never expose profile information,
    # so we set `write_only=True`.
    profile = ProfileSerializer(write_only=True)
    # We want to get the `bio` and `image` fields from the related Profile
    # model.
    bio = serializers.CharField(source='profile.bio', read_only=True)
    image = serializers.CharField(source='profile.image', read_only=True)

    class Meta:
        model = User
        # fields = ('email', 'username', 'password', 'token',)
        fields = (
            'email', 'username', 'password', 'token', 'profile', 'bio',
            'image',
        )

        # The `read_only_fields` option is an alternative for explicitly
        # specifying the field with `read_only=True` like we did for password
        # above. The reason we want to use `read_only_fields` here is because
        # we don't need to specify anything else about the field. For the
        # password field, we needed to specify the `min_length` and
        # `max_length` properties too, but that isn't the case for the token
        # field.
        read_only_fields = ('token',)

    def update(self, instance, validated_data):
        """Performs an update on a User."""

        # Passwords should not be handled with `setattr`, unlike other fields.
        # This is because Django provides a function that handles hashing and
        # salting passwords, which is important for security. What that means
        # here is that we need to remove the password field from the
        # `validated_data` dictionary before iterating over it.
        password = validated_data.pop('password', None)

        # Like passwords, we have to handle profiles separately. To do that,
        # we remove the profile data from the `validated_data` dictionary.
        profile_data = validated_data.pop('profile', {})

        for (key, value) in validated_data.items():
            # For the keys remaining in `validated_data`, we will set them on
            # the current `User` instance one at a time.
            setattr(instance, key, value)

        if password is not None:
            # `.set_password()` is the method mentioned above. It handles all
            # of the security stuff that we shouldn't be concerned with.
            instance.set_password(password)

        # Finally, after everything has been updated, we must explicitly save
        # the model. It's worth pointing out that `.set_password()` does not
        # save the model.
        instance.save()

        for (key, value) in profile_data.items():
            # We're doing the same thing as above, but this time we're making
            # changes to the Profile model.
            setattr(instance.profile, key, value)

        if bio is not None:
            # `.set_password()` is the method mentioned above. It handles all
            # of the security stuff that we shouldn't be concerned with.
            instance.setattr(bio)

            # Save the profile just like we saved the user.
        instance.profile.save()

        return instance

views.py

class UserRetrieveUpdateAPIView(RetrieveUpdateAPIView):
    permission_classes = (IsAuthenticated,)
    renderer_classes = (UserJSONRenderer,)
    serializer_class = UserSerializer

    def retrieve(self, request, *args, **kwargs):
        # There is nothing to validate or save here. Instead, we just want the
        # serializer to handle turning our `User` object into something that
        # can be JSONified and sent to the client.
        serializer = self.serializer_class(request.user)
        return Response(serializer.data, status=status.HTTP_200_OK)

    def update(self, request, *args, **kwargs):
        # serializer_data = request.data.get('user', {})
        user_data = request.data.get('user',{})

        serializer_data = {
            'username': user_data.get('username', request.user.username),
            # 'email': user_data.get('email', request.user.email),
            'password': user_data.get('password', request.user.password),

            'profile': {
                'bio': user_data.get('bio', request.user.profile.bio),
                'image': user_data.get('image', request.user.profile.image)
            }
        }
        # Here is that serialize, validate, save pattern we talked about
        # before.
        serializer = self.serializer_class(
            request.user, data=serializer_data, partial=True
        )
        serializer.is_valid(raise_exception=True)
        serializer.save()

        return Response(serializer.data, status=status.HTTP_200_OK)

我不断得到的答复是:

    response = get_response(request)
  File "C:\Users\dell01\team_work\ah-django-Aqua\environment\lib\site-packages\django\core\handlers\base.py", line 126, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "C:\Users\dell01\team_work\ah-django-Aqua\environment\lib\site-packages\django\core\handlers\base.py", line 124, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Users\dell01\team_work\ah-django-Aqua\environment\lib\site-packages\django\views\decorators\csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "C:\Users\dell01\team_work\ah-django-Aqua\environment\lib\site-packages\django\views\generic\base.py", line 68, in view
    return self.dispatch(request, *args, **kwargs)
  File "C:\Users\dell01\team_work\ah-django-Aqua\environment\lib\site-packages\rest_framework\views.py", line 495, in dispatch
    response = self.handle_exception(exc)
  File "C:\Users\dell01\team_work\ah-django-Aqua\environment\lib\site-packages\rest_framework\views.py", line 455, in handle_exception
    self.raise_uncaught_exception(exc)
  File "C:\Users\dell01\team_work\ah-django-Aqua\environment\lib\site-packages\rest_framework\views.py", line 492, in dispatch
    response = handler(request, *args, **kwargs)
  File "C:\Users\dell01\team_work\ah-django-Aqua\environment\lib\site-packages\rest_framework\generics.py", line 257, in put
    return self.update(request, *args, **kwargs)
  File "C:\Users\dell01\team_work\ah-django-Aqua\authors\apps\authentication\views.py", line 73, in update
    'bio': user_data.get('bio', request.user.profile.bio),
AttributeError: 'User' object has no attribute 'profile'
[02/Apr/2019 17:10:16] "PUT /api/user/ HTTP/1.1" 500 18787

我被困住了,因为我尝试了多种解决方案,所以我需要一个解决方案。

其他信息:当我注释掉views.py中的个人资料位时,会更新用户。

2 个答案:

答案 0 :(得分:0)

问题似乎是: 'profile': { 'bio': user_data.get('bio', request.user.profile.bio), 'image': user_data.get('image', request.user.profile.image) }在您的views.py中。该错误告诉您属性配置文件不存在。

答案 1 :(得分:0)

“用户”对象没有属性“配置文件” ->问题是用户对象的模型中没有配置文件。在您的模型(用户或配置文件)中,可能缺少一对一关系。为了确保您可以直接检查数据库和表Profil或User,以查看其中一个表中是否存在profil_id或user_id。

问题不在您的序列化程序或视图中,而在您的模型中。

我阅读了您的教程。也许您忘了在个人资料模型中添加“用户”关系。

查看模型: enter image description here

也不要忘记运行makemigrations / migration命令;)