Как получить внешний ключ с гиперссылкой в ​​сериализаторе Django REST Framework?

Я пытаюсь сериализовать модель в своем проекте DRF. Модель содержит 2 поля внешнего ключа и несколько полей простого типа данных. Одно из полей внешнего ключа относится к моей модели CustomUser, и это поле отображается в виде гиперссылки в моих выходных данных API, что и ожидается. Другое поле внешнего ключа относится к другой модели (показано ниже). За последние несколько часов я повторял одни и те же ошибки, поскольку пробовал различные решения, рекомендованные в других потоках StackOverflow.

Может ли кто-нибудь показать мне, как заставить этот второй внешний ключ отображаться как ссылка в моем выводе API?

Когда мои файлы кода отображаются так, как показано ниже, я получаю эту ошибку при попытке доступа к URL-адресам localhost:8000/trade/create/ и localhost:8000/trader-accounts/:

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.

Конечно, я несколько раз гуглил эти сообщения об ошибках и их фрагменты, а также залез в несколько кроличьих нор DRF, пробуя разные вещи.

торги/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'),

Когда я удаляю 'trader_account' из api.serializers.TradeSerializer.Meta.fields, я могу успешно получить доступ как к конечной точке 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 есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
0
919
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Исходя из этого 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

Теперь ваши URL-шаблоны должны быть

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, чтобы удалить сделку.

Пробовали ли вы проложить маршрут для account так же, как user?

В сообщении об ошибке, которое вы включили, говорится, что нет маршрута с ожидаемым именем.

Согласно документации на 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
    ...
)
Ответ принят как подходящий

Большое спасибо Габбе за то, что указал мне правильное направление.

Я переименовал 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)

И все работает!

Вы должны были принять его ответ, потому что он нашел время, чтобы помочь вам, и привел вас к рабочему решению.

DragonBobZ 10.01.2021 02:12

Было два существенных различия в том, что предложил Габбе, и в том, что в итоге сработало. 1) Мне нужно было использовать viewsets.ModelViewSet вместо предложенного generics.ListCreateAPIView. 2) URL-адреса должны быть зарегистрированы на маршрутизаторе и не определены явно в urlpatterns. Их ответ в целом был очень полезным, но не все решение в этом случае. Принять это как решение было бы заблуждением для людей, столкнувшихся с этим в будущем.

allardbrain 10.01.2021 03:12

Другие вопросы по теме