URL 查找不适用于更新和检索

URL lookup not working with update and retrieve

在 url /users/*username 我应该能够通过用户名访问每个用户,问题是它没有按预期工作,不管我写什么在 *username 中它将 update/delete 当前登录的用户。

示例: 我可以通过 /users/123/ 更新用户“admin”,即使 123 显然不是 admin 或任何其他现有用户。

这是用户视图,其中的方法未按我的预期方式工作:

class UserViewSet(mixins.RetrieveModelMixin,
                  mixins.UpdateModelMixin,
                  mixins.DestroyModelMixin,
                  viewsets.GenericViewSet):

    serializer_class = UserSerializer
    lookup_field = 'username'

    queryset = User.objects.all()

    def get_permissions(self):
        if self.action in ['signup', 'login',]:
            permissions = [AllowAny]
        elif self.action in ['retrieve', 'update', 'destroy']:
            permissions = [IsAuthenticated, IsSameUser]
        else:
            permissions = [IsAuthenticated]
        return [p() for p in permissions]


    def retrieve(self, request, *args, **kwargs):
        response = super(UserViewSet, self).retrieve(request, *args, **kwargs)
        data = response.data
        return Response(data=data, status=status.HTTP_200_OK)

    def update(self, request, *args, **kwargs):
        serializer = UserModelSerializer(request.user, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data, status=status.HTTP_200_OK)

用户URL:

""" Users URLs """

# Django
from django.urls import include, path
# Django REST Framework
from rest_framework.routers import DefaultRouter
# Views
from users.views import UserViewSet

router = DefaultRouter()
router.register(r'users/', UserViewSet, basename='users')

urlpatterns = [
    path('', include(router.urls)),
]

it will update/delete the current logged user.

当然

您的更新方法是:

def update(self, request, *args, **kwargs):
    serializer = UserModelSerializer(request.user, data=request.data, partial=True)
    serializer.is_valid(raise_exception=True)
    serializer.save()
    return Response(serializer.data, status=status.HTTP_200_OK)

其中 request.user 是登录用户。


正确的代码是这样的:

class UserViewSet(mixins.RetrieveModelMixin,
                  mixins.UpdateModelMixin,
                  mixins.DestroyModelMixin,
                  viewsets.GenericViewSet):

    serializer_class = UserSerializer
    lookup_field = 'username'

    queryset = User.objects.all()

    ...
    
    def update(self, url_username, request, *args, **kwargs):
        try:
            user_object = self.queryset.get(username=url_username)
        except User.DoesNotExist:
             Response({'error': 'not found'}, status=404)
        serializer = UserModelSerializer(user_object, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data, status=status.HTTP_200_OK)

还需要在URL路径中添加url_username:

urlpatterns = [
    ...,
    path('users/<str:url_username>', update_user)
]

这里

  • user_object 是您要更新的用户
  • request.data 是要设置给现有用户的新数据
  • url_username/users/*username 中的 *username 部分,用于获取准确的用户

更新 1:回答问题 →

I'm using DRF default router (updated the post and added what my urls.py looks like), the URL is generated taking the actions from the viewset, should I make all the URL like you did there? Like it says in the documentation all the URL patterns should be done automatically.

Oohhh...Django 有数百万种方法来实现 API 方法。根据一段代码在我的脑海中重现行为是非常复杂的。我的答案的关键是你应该使用 username 放在 url 而不是 request.user 中。因为 request.user 是登录用户对象(执行请求的用户)。

首先,试着理解UpdateModelMixin

Mixin 提供了正确且标准的 update() 方法。
您试图通过包含 mixins.UpdateModelMixin 并在之后创建(覆盖)自己的 update() 来添加标准 update() 方法。所以包含 UpdateModelMixin 是没用的。

如果您想使用标准 update 方法,只需完全删除自己的 def update():。这可能是解决方案。

如果要创建自定义 update 方法,请删除 UpdateModelMixin。没必要。

在这种情况下尝试更改更新参数,例如:

def update(self, request, pk, *args, **kwargs):
    try:
        user_object = self.queryset.get(username=pk)
    ...

在你的情况下 pk 将是 username(因为你设置了 lookup_field = 'username'

正如我之前所说,很难预测 Django 的行为(实际上我不记得详细的工作原理 pk)。试试看会发生什么。

如果 pk 如我所料,您不需要编辑 urlpatterns

PS def retrieve(self, request, *args, **kwargs): 如果你包括 RetreiveModelMixin 也是不必要的。 RetreiveModelMixin 为您添加 retrieve() 方法。

在你的 UserSerializer 序列化器中,你应该添加这个元 class:

    class Meta:
        lookup_field = 'username'