Я пытаюсь сериализовать модель в своем проекте 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'
Я так долго смотрел на это, что не могу найти ошибку. Очень ценю помощь!
Исходя из этого 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)
И все работает!
Было два существенных различия в том, что предложил Габбе, и в том, что в итоге сработало. 1) Мне нужно было использовать viewsets.ModelViewSet
вместо предложенного generics.ListCreateAPIView
. 2) URL-адреса должны быть зарегистрированы на маршрутизаторе и не определены явно в urlpatterns
. Их ответ в целом был очень полезным, но не все решение в этом случае. Принять это как решение было бы заблуждением для людей, столкнувшихся с этим в будущем.
Вы должны были принять его ответ, потому что он нашел время, чтобы помочь вам, и привел вас к рабочему решению.