You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用DRF Retrieve视图集无需PK/UUID获取商家单对象的问题

问题分析与解决方案

错误原因

你当前的问题核心在于路由与视图方法的绑定不匹配:

  • 使用DefaultRouter注册MerchantProfileViewSet后,RetrieveModelMixin对应的URL是/merchant/profile/<uuid>/(必须传入uuid参数),而你期望访问/merchant/profile/(无参数)就能获取当前用户的资料,这个路径默认绑定的是ListModelMixin的list方法,所以你重写的retrieve逻辑根本没被执行。
  • 另外,你重写的retrieve方法未处理MerchantProfile.DoesNotExist异常,而list方法做了异常捕获,这也是两者表现不同的原因之一。

解决方案

方案1:重写get_object并自定义路由(推荐,保留ViewSet特性)

通过重写get_object方法让Retrieve逻辑基于当前用户,再修改路由让根路径指向Retrieve动作:

  1. 修改views.py:
from rest_framework.exceptions import Http404

class MerchantProfileViewSet(AuthenticationMixin, RetrieveModelMixin, UpdateModelMixin, GenericViewSet):
    queryset = MerchantProfile.objects.all()
    serializer_class = MerchantProfileSerializer
    lookup_field = "uuid"

    def get_object(self):
        try:
            # 通过当前登录用户关联获取商家资料
            return MerchantProfile.objects.get(merchant__user=self.request.user)
        except MerchantProfile.DoesNotExist:
            raise Http404("Merchant profile does not exist for the current user")
  1. 自定义路由,让根路径绑定Retrieve/Update动作:
from rest_framework.routers import DefaultRouter, Route

class CustomMerchantRouter(DefaultRouter):
    routes = [
        Route(
            url=r'^{prefix}{trailing_slash}$',
            mapping={'get': 'retrieve', 'put': 'update', 'patch': 'partial_update'},
            name='{basename}-detail',
            detail=False,
            initkwargs={'suffix': 'Detail'}
        ),
    ]

merchant_router = CustomMerchantRouter()
merchant_router.register('/profile', MerchantProfileViewSet, basename="merchantprofile")

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

现在访问/merchant/profile/会直接触发Retrieve逻辑,同时保留Update/Patch功能。

方案2:添加自定义Action(灵活,保留多场景支持)

如果需要同时保留「通过uuid获取任意商家资料」和「无参数获取当前用户资料」的功能,可以用@action装饰器添加自定义动作:

from rest_framework.decorators import action
from rest_framework.exceptions import Http404

class MerchantProfileViewSet(AuthenticationMixin, RetrieveModelMixin, UpdateModelMixin, GenericViewSet):
    queryset = MerchantProfile.objects.all()
    serializer_class = MerchantProfileSerializer
    lookup_field = "uuid"

    @action(detail=False, methods=['get'])
    def me(self, request):
        try:
            instance = MerchantProfile.objects.get(merchant__user=request.user)
            serializer = self.get_serializer(instance)
            return Response(serializer.data, status=status.HTTP_200_OK)
        except MerchantProfile.DoesNotExist:
            raise Http404("Merchant profile does not exist for the current user")

此时路由会生成/merchant/profile/me/,访问该路径即可获取当前用户资料,原/merchant/profile/<uuid>/仍可通过uuid查询其他商家资料。

方案3:改用RetrieveUpdateAPIView(轻量,适合单一场景)

如果只需要「获取/更新当前用户资料」的功能,无需ViewSet的其他特性,直接用通用视图更简洁:

from rest_framework.generics import RetrieveUpdateAPIView
from rest_framework.exceptions import Http404

class MerchantProfileView(AuthenticationMixin, RetrieveUpdateAPIView):
    serializer_class = MerchantProfileSerializer

    def get_object(self):
        try:
            return MerchantProfile.objects.get(merchant__user=self.request.user)
        except MerchantProfile.DoesNotExist:
            raise Http404("Merchant profile does not exist for the current user")

路由配置:

urlpatterns = [
   path('merchant/profile/', MerchantProfileView.as_view(), name='merchantprofile'),
]

额外优化建议

  1. 避免裸异常捕获:永远不要用except:捕获所有异常,应明确捕获MerchantProfile.DoesNotExist,防止隐藏未知错误。
  2. 修复Merchant模型的__str__方法:原方法依赖self.merchantprofile,若商家未创建资料会抛出异常,建议修改为:
def __str__(self):
    return getattr(self.merchantprofile, 'organization_name', f'Merchant {self.uuid}')
  1. Serializer字段验证:可在MerchantProfileSerializer中添加字段格式验证(如手机号正则),提升API健壮性。

内容的提问来源于stack exchange,提问作者henry

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 15:23:13