55896


Django REST frameworkについて質問です。

現在、ユーザのパスワード変更するAPIを作成しております。

APIには引数として以下3つをリクエストします。

  * 現在のパスワード (old_password)
  * 新しいパスワード (new_password)
  * 新しいパスワード(確認用) (new_password_confirm)

APIの処理の流れとしては以下になります。

  1. 新しいパスワード(new_password)と新しいパスワード(確認用)(new_password_confirm)の入力値が同じかチェック
  2. 現在のパスワード(old_password)と対象ユーザのDB登録値パスワードが同じチェック
  3. 新しいパスワード(new_password)でユーザ情報を更新する

しかしながら現在、AttributeErrorが発生しています。  
親クラスのmixins.py(view.pyのclass ChangePassViewで継承している  
generics RetrieveUpdateAPIViewの更に上の親クラス)  
で発生しているようです。  
もしエラーの原因についてご存知の方がいらっしゃいましたら、ご教授頂きたく存じ上げます。

### django.log(エラー文)

[code]

     File "/usr/local/lib/python3.6/site-packages/rest_framework/views.py", line 495, in dispatch
        response = self.handle_exception(exc)
      File "/usr/local/lib/python3.6/site-packages/rest_framework/views.py", line 455, in handle_exception
        self.raise_uncaught_exception(exc)
      File "/usr/local/lib/python3.6/site-packages/rest_framework/views.py", line 492, in dispatch
        response = handler(request, *args, **kwargs)
      File "./api/views.py", line 76, in patch
        return super().update(new_password, kwargs['pk'])
      File "/usr/local/lib/python3.6/site-packages/rest_framework/mixins.py", line 68, in update
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
    AttributeError: 'str' object has no attribute 'data'
    
[/code]

### views.py

[code]

    class ChangePassView(generics.RetrieveUpdateAPIView):
    
        queryset = User.objects.filter.all()
        serializer_class = ChangePassSerializer
    
        def patch(self, request, *args, **kwargs):
    
            old_password = request.POST["old_password"]
            new_password = request.POST["new_password"]
            new_password_confirm = request.POST["new_password_confirm"]
    
            if new_password != (new_password_confirm):
                 return JsonResponse({'message': '新しいパスワードが異なります'}, status=403)
    
            user = User.objects.get(id=kwargs['pk'])
    
            if not user.check_password(old_password):
                 return JsonResponse({'message': '現在のパスワードが異なります'}, status=403)
    
            return super().update(new_password, kwargs['pk'])
    
[/code]

### mixins.py(親クラス、エラーは下記貼り付け箇所だと8行目)

[code]

    class UpdateModelMixin(object):
        """
        Update a model instance.
        """
        def update(self, request, *args, **kwargs):
            partial = kwargs.pop('partial', False)
            instance = self.get_object()
            serializer = self.get_serializer(instance, data=request.data, partial=partial)
            serializer.is_valid(raise_exception=True)
            self.perform_update(serializer)
    
            if getattr(instance, '_prefetched_objects_cache', None):
                # If 'prefetch_related' has been applied to a queryset, we need to
                # forcibly invalidate the prefetch cache on the instance.
                instance._prefetched_objects_cache = {}
    
            return Response(serializer.data)
    
        def perform_update(self, serializer):
            serializer.save()
    
        def partial_update(self, request, *args, **kwargs):
            kwargs['partial'] = True
            return self.update(request, *args, **kwargs)
    
[/code]

### models.py

[code]

    class User(AbstractBaseUser, PermissionsMixin):
        user_Id = models.CharField(_('user_Id'), max_length=7, unique=True)
        password = models.CharField(_('password'), max_length=255)
    
[/code]

### serializer.py

[code]

    class ChangePassSerializer(serializers.ModelSerializer):
    
        old_password = serializers.CharField(max_length=255)
        new_password = serializers.CharField(max_length=255)
        new_password_confirm = serializers.CharField(max_length=255)
    
        class Meta:
            model = User
            fields = ('password',)
    
        def update(self, user, validated_data):
            user.set_password(validated_data['password'])
            user.save()
            return user
    
[/code]

### urls.py

[code]

    urlpatterns = [
      app_name = 'api'
      path('password_change/<int:pk>/', ChangePassView.as_view(),
            name='password_change'),
    ]
    
[/code]


CC BY-SA 4.0
2019-06-17T22:42:25.000
55888
2020-02-12T07:01:00.110
2020-06-17T08:14:45.997
-1
34673
question
python api django rest framework
Django REST frameworkでAttributeError


エラーの原因は`views.py`の次の行（最終行）です。

[code]

    return super().update(new_password, kwargs['pk'])
    
[/code]

`super().update()`で親クラス（`RetrieveUpdateAPIView`）の親クラス（`UpdateModelMixin`）の次のメソッドが呼び出されますが、

[code]

    def update(self, request, *args, **kwargs):
    
[/code]

このメソッドが想定する`request`ではなく、`str`型の`new_password`が渡ってくるので、@PicoSushiさんがコメントされている理由により、`AttributeError`がスローされます。

なので、おそらく`views.py`の最終行は次のようにすべきかと思います。

[code]

    return super.update(request, *args, **kwargs)
    
[/code]

※ここからは`mixins.py`を見た上での推測です。

`UpdateModelMixin.update()`が以下のような順で、最終的に`ChangePassSerializer.save()`を呼び出すので、

[code]

    self.perform_update(serializer)
    
[/code]

↓

[code]

    serializer.save()
    
[/code]

この`save()`にパスワードを更新する処理を実装すればよさそうな気がします。

入力チェックの実装箇所など他にも修正した方がいいように思う部分もあるので、DRFのガイドをよく読んでから実装することをお勧めします。


CC BY-SA 4.0
2019-06-18T07:59:16.497
55896
2019-06-18T08:06:37.737
2019-06-18T08:06:37.737
21092
21092
55888
answer