【问题标题】:How do I get a hyperlinked foreign key in Django REST Framework serializer?如何在 Django REST Framework 序列化程序中获取超链接外键?
【发布时间】:2020-12-14 03:56:10
【问题描述】:

我正在尝试序列化我的 DRF 项目中的模型。该模型包含 2 个外键字段和一些简单的数据类型字段。外键字段之一是我的CustomUser 模型,该字段在我的 API 输出中显示为超链接,这是预期的。另一个外键字段指向不同的模型(如下所示)。在过去的几个小时里,我一直在重复同样的几个错误,因为我尝试了其他 StackOverflow 线程中推荐的各种解决方案。

有人可以告诉我如何让第二个外键作为链接出现在我的 API 输出中吗?

当我的代码文件如下所示时,我在尝试访问 localhost:8000/trade/create/localhost:8000/trader-accounts/ URL 时收到此错误:

django.core.exceptions.ImproperlyConfigured: Could not resolve URL for hyperlinked relationship using view name "traderaccount-detail". You may have failed to include the related model in your API, or incorrectly configured the `lookup_field` attribute on this field.

当然,我多次在 Google 上搜索此错误消息及其部分内容,并尝试了多个 DRF 兔子洞。

trades/models.py

class Trade(models.Model):
    trade_owner = models.ForeignKey(CustomUser, related_name='owners', on_delete=models.RESTRICT)
    trade_account = models.ForeignKey(TraderAccount, related_name='trades', on_delete=models.RESTRICT)

trader_account/models.py

class TraderAccount(models.Model):

    account_owner = models.ForeignKey(
        CustomUser, on_delete=models.CASCADE,
    )
    account_name = models.CharField(
        max_length=200, blank=True, verbose_name='Account Name'
    )

api/serializers.py

class TraderAccountSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = TraderAccount
        fields = ['account_owner', 'account_name',]


class TradeSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Trade
        fields = ['trade_owner', 'trade_account', 'magic_number',]

api/views.py

class TraderAccountView(generics.ListCreateAPIView):
    permission_classes = (IsAuthenticated,)
    queryset = TraderAccount.objects.all()
    serializer_class = TraderAccountSerializer


class TradeCreateView(generics.ListCreateAPIView):
    permission_classes = (IsAuthenticated,)
    queryset = Trade.objects.all()
    serializer_class = TradeSerializer

api/urls.py


router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'groups', views.GroupViewSet)

urlpatterns = [
    path('', include(router.urls)),
    path('trader-accounts/', views.TraderAccountView.as_view(), name='trader-accounts'),
    path('trade/create/', views.TradeCreateView.as_view(), name='trade-create'),

当我从api.serializers.TradeSerializer.Meta.fields 中删除'trader_account' 时,我可以成功访问localhost:8000/trade/create/ 端点和localhost:8000/trader-accounts/ 端点。

api/serializers.py

# Omit TraderAccountSerializer class for brevity in this example


class TradeSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Trade
        fields = ['trade_owner', 'magic_number',]  # Removed 'trader_account'

我一直在看这个,现在我找不到错误。非常感谢您的帮助!

【问题讨论】:

    标签: python django django-rest-framework


    【解决方案1】:

    基于此api/views.py,您有一个TraderAccountView(generics.ListCreateAPIView)。 您应该将其重命名为TraderAccountList(generics.ListCreateAPIView),然后为详细信息添加一个额外的视图; TraderAccountDetail(generics.RetrieveUpdateDestroyAPIView)

    api/views.py 现在是:

    class TraderAccountList(generics.ListCreateAPIView):
        permission_classes = (IsAuthenticated,)
        queryset = TraderAccount.objects.all()
        serializer_class = TraderAccountSerializer
    
    class TraderAccountDetail(generics.RetrieveUpdateDestroyAPIView):
        permission_classes = (IsAuthenticated,)
        queryset = TraderAccount.objects.all()
        serializer_class = TraderAccountSerializer
    

    你的 urlpatterns 现在应该是

    router = routers.DefaultRouter()
    router.register(r'users', views.UserViewSet)
    router.register(r'groups', views.GroupViewSet)
    
    urlpatterns = [
        path('', include(router.urls)),
        path('trader-accounts/', views.TraderAccountList.as_view(), name='trader-accounts'),
        path('trader-accounts/<int:pk>/', views.TraderAccountDetail.as_view()),
    ]
    

    并删除 TradeCreate。因为您现在正在构建一个 RESTful API。要创建交易,您可以向列表视图发出 POST 请求以创建新交易。

    在列表视图中,您可以执行 GET 来获取所有交易的列表,或者执行 POST 来创建新交易。 在详细视图中,您可以执行 GET 来获取特定交易,或者执行 PUT 或 PATCH 来更新,或者执行 DELETE 来删除交易。

    【讨论】:

      【解决方案2】:

      您是否尝试过以与user 相同的方式为account 创建路由?

      您包含的错误消息表明没有具有预期名称的路由。

      根据https://www.django-rest-framework.org/api-guide/serializers/#how-hyperlinked-views-are-determined 的文档,要使自动映射工作,您需要一个名称为account-detail 的视图,该视图采用1 个整数路由参数。

      正如他们所说,超链接的序列化程序很繁琐,因此请密切注意文档。如果您更改视图名称,您可以在序列化程序中覆盖它。

      account = serializers.HyperlinkedRelatedField(
          view_name='account-detail', # or whatever name you give it
          ...
      )
      

      【讨论】:

        【解决方案3】:

        非常感谢 Gabbeh 为我指明了正确的方向。

        我将TraderAccountView(generics.ListCreateAPIView) 重命名为TraderAccountList(viewsets.ModelViewSet) 并将此视图添加到api/views.py

        class TraderAccountDetail(generics.RetrieveUpdateDestroyAPIView):
            permission_classes = (IsAuthenticated,)
            queryset = TraderAccount.objects.all()
            serializer_class = TraderAccountSerializer
        

        api/urls.py 文件现在是:

        router = routers.DefaultRouter()
        router.register(r'users', views.UserViewSet)
        router.register(r'groups', views.GroupViewSet)
        router.register(r'trader-accounts', views.TraderAccountList)
        

        一切正常!

        【讨论】:

        • 您应该接受他的回答,因为他花时间帮助您并引导您找到可行的解决方案。
        • Gabbeh 的建议和最终奏效的方法存在 2 个重大差异。 1) 我需要使用viewsets.ModelViewSet 而不是建议的generics.ListCreateAPIView。 2) URL 需要注册到路由器,并且没有在urlpatterns 中明确定义。他们的整体反应非常有帮助,但在这种情况下不是整个解决方案。接受它作为解决方案会误导人们在未来遇到这个问题。
        猜你喜欢
        • 1970-01-01
        • 2015-07-24
        • 2019-01-19
        • 1970-01-01
        • 2016-04-10
        • 2016-10-03
        • 1970-01-01
        • 2017-10-05
        相关资源
        最近更新 更多