Django Rest Framework - nie można rozwiązać adresu URL dla relacji z hiperłączem przy użyciu nazwy widoku „szczegóły użytkownika”

108

Buduję projekt w Django Rest Framework, w którym użytkownicy mogą się zalogować, aby zobaczyć swoją piwnicę z winami. Moje zestawy ModelViewSets działały dobrze i nagle pojawia się ten frustrujący błąd:

Nie można rozwiązać adresu URL relacji hiperłącza przy użyciu nazwy widoku „szczegóły użytkownika”. Być może nie udało się uwzględnić powiązanego modelu w interfejsie API lub nieprawidłowo skonfigurowano lookup_fieldatrybut w tym polu.

Dane śledzenia pokazują:

    [12/Dec/2013 18:35:29] "GET /bottles/ HTTP/1.1" 500 76677
Internal Server Error: /bottles/
Traceback (most recent call last):
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/django/core/handlers/base.py", line 114, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/viewsets.py", line 78, in view
    return self.dispatch(request, *args, **kwargs)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/django/views/decorators/csrf.py", line 57, in wrapped_view
    return view_func(*args, **kwargs)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/views.py", line 399, in dispatch
    response = self.handle_exception(exc)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/views.py", line 396, in dispatch
    response = handler(request, *args, **kwargs)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/mixins.py", line 96, in list
    return Response(serializer.data)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/serializers.py", line 535, in data
    self._data = [self.to_native(item) for item in obj]
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/serializers.py", line 325, in to_native
    value = field.field_to_native(obj, field_name)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/relations.py", line 153, in field_to_native
    return self.to_native(value)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/relations.py", line 452, in to_native
    raise Exception(msg % view_name)
Exception: Could not resolve URL for hyperlinked relationship using view 
name "user-detail". You may have failed to include the related model in 
your API, or incorrectly configured the `lookup_field` attribute on this 
field.

Mam niestandardowy model użytkownika poczty e-mail, a model butelki w models.py to:

class Bottle(models.Model):    
      wine = models.ForeignKey(Wine, null=False)
      user = models.ForeignKey(User, null=False, related_name='bottles')

Moje serializatory:

class BottleSerializer(serializers.HyperlinkedModelSerializer):

    class Meta:
        model = Bottle
        fields = ('url', 'wine', 'user')

class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = User
        fields = ('email', 'first_name', 'last_name', 'password', 'is_superuser')

Moje poglądy:

class BottleViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows bottles to be viewed or edited.
    """
    queryset = Bottle.objects.all()
    serializer_class = BottleSerializer

class UserViewSet(ListCreateAPIView):
    """
    API endpoint that allows users to be viewed or edited.
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer

i na końcu adres URL:

router = routers.DefaultRouter()
router.register(r'bottles', views.BottleViewSet, base_name='bottles')

urlpatterns = patterns('',
    url(r'^', include(router.urls)),
    # ...

Nie mam widoku szczegółów użytkownika i nie wiem, skąd może pochodzić ten problem. Jakieś pomysły?

Dzięki

bpipat
źródło

Odpowiedzi:

96

Ponieważ jest to HyperlinkedModelSerializerTwoja serializer próbuje rozwiązać adres URL powiązany Userna własną Bottle.
Ponieważ nie masz widoku szczegółów użytkownika, nie możesz tego zrobić. Stąd wyjątek.

  1. Czy samo zarejestrowanie UserViewSetroutera nie rozwiązałoby problemu?
  2. Możesz zdefiniować pole użytkownika w swoim, BottleSerializeraby jawnie używać UserSerializerzamiast próbować rozwiązać adres URL. Zobacz dokumentację serializatora o tym, jak radzić sobie z zagnieżdżonymi obiektami .
Carlton Gibson
źródło
1
Wielkie dzięki, skomentowałem zestaw UserViewSet w moich routerach, co rozwiązało problem!
bpipat
5
O to chodzi - zrób to wyraźnie - zbyt wiele magii to zbyt duża strata czasu.
andilabs
Czy możesz wskazać, co jest błędnie skonfigurowane w moim projekcie ?
JJD,
@ GrijeshChauhan - Dzięki! Teraz naprawione.
Carlton Gibson
Powodem, dla którego to nie działało, było to, że django chciał pokazać powiązane dane od użytkownika w twoim bieżącym widoku dla parametru User. Zwykle wybiera listę dostępnych wartości. Ponieważ UserViewSet nie został zdefiniowany, nie można było pobrać szczegółów w celu renderowania strony internetowej. Dodanie UserViewSet i zarejestrowanie się pod domyślnym routerem sprawia, że ​​renderowanie wszystkich komponentów jest kompletne.
Doogle
65

Natknąłem się również na ten błąd i rozwiązałem go w następujący sposób:

Powodem jest to, że zapomniałem nadać "** - detail" (nazwa_widoku, np .: szczegóły użytkownika) przestrzeni nazw. Tak więc Django Rest Framework nie mógł znaleźć tego widoku.

W moim projekcie jest jedna aplikacja, przypuśćmy, że nazwa mojego projektu to myproject, a nazwa aplikacji to myapp.

Istnieją dwa pliki urls.py, jeden to, myproject/urls.pya drugi to myapp/urls.py. Daję aplikacji przestrzeń nazw w myproject/urls.py, tak jak:

url(r'', include(myapp.urls, namespace="myapp")),

Zarejestrowałem pozostałe routery szkieletowe w myapp/urls.py, a następnie otrzymałem ten błąd.

Moim rozwiązaniem było jawne podanie adresu URL z przestrzenią nazw:

class UserSerializer(serializers.HyperlinkedModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name="myapp:user-detail")

    class Meta:
        model = User
        fields = ('url', 'username')

I to rozwiązało mój problem.

bovenson
źródło
@boveson, to działa jak urok! Dzięki, rozwiązałeś wiele godzin frustracji po mojej stronie.
lmiguelvargasf
To również sprawiło, że działało to dla mnie. Ważnym punktem po mojej stronie była również poprawna pisownia nazwy base_name na trasie!
maggie
1
Kluczem tutaj jest prefiks przestrzeni nazw, który zapobiega działaniu reverse .....
boatcoder
Miałem taki problem, a ta odpowiedź rozwiązała mój problem po 3 godzinach wyszukiwania! @bovenson
Whale 52 Hz
lub możesz skorzystać z extra_kwargs zgodnie z zaleceniami drf:extra_kwargs = {'url': {'view_name': 'myapp:user-detail'}}
ChrisRob
19

Może ktoś mógłby rzucić okiem na to: http://www.django-rest-framework.org/api-guide/routers/

Jeśli używasz przestrzeni nazw z serializatorami z hiperłączami, musisz również upewnić się, że wszystkie parametry view_name w serializatorach poprawnie odzwierciedlają przestrzeń nazw. Na przykład:

urlpatterns = [
    url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),
    url(r'^api/', include(router.urls, namespace='api')),
]

należałoby dołączyć parametr, taki jak view_name='api:user-detail'pola serializatora, które są połączone hiperłączem z widokiem szczegółów użytkownika.

class UserSerializer(serializers.HyperlinkedModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name="api:user-detail")

class Meta:
    model = User
    fields = ('url', 'username')
JackPy
źródło
1
Podsumowując, nadanie api przestrzeni nazw powoduje błąd w tytule, prawdopodobnie nie chcesz tego robić, chyba że chcesz to zmienić w wielu miejscach.
Mark
pracował dla mnie! mój urls.pybył podwójnie zagnieżdżone w moim newsiteprojekcie: (1) newsite/urls.py(stworzony przez Django) (2) polls/urls.py(3) polls/api/v1/urls.py ............ muszę wspomnieć zagnieżdżony nazwę zaurl = serializers.HyperlinkedIdentityField(view_name="polls:polls_api:user-detail")
Grijesh Chauhan
12

Innym paskudnym błędem, który powoduje ten błąd, jest niepotrzebne zdefiniowanie nazwy base_name w twoim urls.py. Na przykład:

router.register(r'{pathname}', views.{ViewName}ViewSet, base_name='pathname')

Spowoduje to błąd opisany powyżej. Pobierz tę nazwę base_name i wróć do działającego API. Poniższy kod naprawiłby błąd. Brawo!

router.register(r'{pathname}', views.{ViewName}ViewSet)

Jednak prawdopodobnie nie dodałeś arbitralnie nazwy base_name, mogłeś to zrobić, ponieważ zdefiniowałeś niestandardową def get_queryset () dla widoku, a więc Django nakazuje dodanie nazwy base_name. W takim przypadku musisz jawnie zdefiniować „url” jako HyperlinkedIdentityField dla danego serializatora. Zauważ, że definiujemy to HyperlinkedIdentityField NA SERIALIZATORZE widoku, który zgłasza błąd. Jeśli mój błąd to „Nie można rozwiązać adresu URL relacji hiperłącza przy użyciu nazwy widoku„ szczegóły badania ”. Możliwe, że nie udało się uwzględnić powiązanego modelu w interfejsie API lub nieprawidłowo skonfigurowano lookup_fieldatrybut w tym polu.” Mogę to naprawić za pomocą następującego kodu.

Mój ModelViewSet (niestandardowy get_queryset jest powodem, dla którego musiałem dodać base_name do router.register () w pierwszej kolejności):

class StudyViewSet(viewsets.ModelViewSet):
    serializer_class = StudySerializer

    '''custom get_queryset'''
    def get_queryset(self):
        queryset = Study.objects.all()
        return queryset

Rejestracja mojego routera dla tego ModelViewSet w urls.py:

router.register(r'studies', views.StudyViewSet, base_name='studies')

I TU GDZIE SĄ PIENIĄDZE! Wtedy mógłbym rozwiązać to tak:

class StudySerializer(serializers.HyperlinkedModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name="studies-detail")
    class Meta:
        model = Study
        fields = ('url', 'name', 'active', 'created',
              'time_zone', 'user', 'surveys')

Tak. Musisz jawnie zdefiniować ten HyperlinkedIdentityField w sobie, aby działał. I musisz się upewnić, że view_namezdefiniowane w HyperlinkedIdentityField jest takie samo, jak zdefiniowane base_namew urls.py z dodanym po nim „-detail”.

Colton Hicks
źródło
2
U mnie to zadziałało, jednak musiałem postawić pełną trasę <app_name>:studies-detail. Na przykład my, jeśli wywoływana jest moja aplikacja tanks, pełna ścieżka będzie wyglądać tak HyperlinkedIdentityField(view_name="tanks:studies-detail"). Aby to zrozumieć, użyłem show_urls polecenia django-exensions , aby zobaczyć pełną trasę i etykietę, którą router automatycznie tworzył.
dtasev
10

Ten kod też powinien działać.

class BottleSerializer(serializers.HyperlinkedModelSerializer):

  user = UserSerializer()

  class Meta:
    model = Bottle
    fields = ('url', 'wine', 'user')
caglar
źródło
3
Warto zauważyć, że UserSerializermusi zostać zaimplementowane (nie jest gotowe do importu), jak pokazano w django-rest-framework.org/api-guide/serializers
Caumons
To zadziałało dla mnie, ale aby to zadziałało, musiałem zmienić router.register (r'bottles ', views.BottleViewSet, base_name =' bottles ') na router.register (r'bottles', views.BottleViewSet). Nie wiem, dlaczego ta zmiana była wymagana.
manpikin
4

Wystąpił ten błąd po dodaniu przestrzeni nazw do mojego adresu URL

 url('api/v2/', include('api.urls', namespace='v2')),

i dodając app_name do mojego urls.py

Rozwiązałem to, określając NamespaceVersioning dla mojego interfejsu API reszty w settings.py mojego projektu

REST_FRAMEWORK = {
    'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.NamespaceVersioning'}
Kelechukwu Nwosu
źródło
3

Dzisiaj mam ten sam błąd i poniższe zmiany mnie ratują.

Zmiana

class BottleSerializer(serializers.HyperlinkedModelSerializer):

do:

 class BottleSerializer(serializers.ModelSerializer):
Manish Pal
źródło
2

Ten sam błąd, ale inny powód:

Definiuję niestandardowy model użytkownika, nic nowego:

from django.contrib.auth.models import (AbstractUser)
class CustomUser(AbstractUser):
    """
    custom user, reference below example
    https://github.com/jonathanchu/django-custom-user-example/blob/master/customuser/accounts/models.py

    # original User class has all I need
    # Just add __str__, not rewrite other field
    - id
    - username
    - password
    - email
    - is_active
    - date_joined
    - method, email_user
    """

    def __str__(self):
        return self.username

To jest moja funkcja widoku:

from rest_framework import permissions
from rest_framework import viewsets
from .models import (CustomUser)
class UserViewSet(viewsets.ModelViewSet):
    permission_classes = (permissions.AllowAny,)
    serializer_class = UserSerializer

    def get_queryset(self):
        queryset = CustomUser.objects.filter(id=self.request.user.id)
        if self.request.user.is_superuser:
            queryset = CustomUser.objects.all()
        return queryset

Ponieważ nie poddałem się querysetbezpośrednio UserViewSet, muszę ustawić, base_namekiedy rejestruję ten zestaw widoków. Oto, gdzie mój komunikat o błędzie spowodowany przez urls.pyplik:

from myapp.views import (UserViewSet)
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'users', UserViewSet, base_name='customuser')  # <--base_name needs to be 'customuser' instead of 'user'

Potrzebujesz tego base_namesamego, co nazwa twojego modelu - customuser.

Pasować
źródło
Stary post, ale Twój komentarz „# <- base_name musi być„ customuser ”zamiast„ user ””, co uratowało mój dzień. Dzięki!
Hannon César
1

Jeśli rozszerzasz klasy GenericViewSet i ListModelMixin i masz ten sam błąd podczas dodawania pola adresu URL w widoku listy, dzieje się tak, ponieważ nie definiujesz widoku szczegółów. Upewnij się, że rozszerzasz składnię RetrieveModelMixin :

class UserViewSet (mixins.ListModelMixin,
                   mixins.RetrieveModelMixin,
                   viewsets.GenericViewSet):
Rowinson Gallego
źródło
1

Wydaje się, że HyperlinkedModelSerializernie zgadzają się na posiadanie ścieżki namespace. W mojej aplikacji dokonałem dwóch zmian.

# rootapp/urls.py
urlpatterns = [
    # path('api/', include('izzi.api.urls', namespace='api'))
    path('api/', include('izzi.api.urls')) # removed namespace
]

W zaimportowanym pliku adresów URL

# app/urls.py
app_name = 'api' // removed the app_name

Mam nadzieję że to pomoże.

Cody Wikman
źródło
0

Napotkałem ten sam błąd, gdy korzystałem z przewodnika szybkiego startu DRF http://www.django-rest-framework.org/tutorial/quickstart/, a następnie próbowałem przejść do katalogu / users. Robiłem tę konfigurację wiele razy bez problemów.

Moje rozwiązanie nie było w kodzie, ale w zastąpieniu bazy danych.

Różnica między tą instalacją a innymi wcześniejszymi polegała na utworzeniu lokalnej bazy danych.

Tym razem uruchomiłem mój

./manage.py migrate
./manage.py createsuperuser

natychmiast po uruchomieniu

virtualenv venv
. venv/bin/activate
pip install django
pip install djangorestframework

Zamiast dokładnej kolejności podanej w przewodniku.

Podejrzewałem, że coś nie zostało poprawnie utworzone w DB. Nie przejmowałem się moją bazą deweloperską, więc usunąłem ją i uruchomiłem ./manage.py migratepolecenie jeszcze raz, utworzyłem superużytkownika, przeszukałem do / users i błąd zniknął.

Coś było problematyczne z kolejnością operacji, w których skonfigurowałem DRF i db.

Jeśli używasz sqlite i jesteś w stanie przetestować zmianę na świeżą bazę danych, warto spróbować, zanim przejdziesz do sekcji całego kodu.

Ben Havilland
źródło
0

Bottle = serializers.PrimaryKeyRelatedField (read_only = True)

read_only pozwala na reprezentowanie pola bez konieczności łączenia go z innym widokiem modelu.

Cristian Fernando
źródło
0

Otrzymałem ten błąd w DRF 3.7.7, gdy wartość slug była pusta (równa się „”) w bazie danych.

mrmuggles
źródło
0

generics.RetrieveAPIViewNapotkałem ten sam problem i rozwiązałem go, dodając jako klasę bazową do mojego zestawu widoków.

Jace Browning
źródło
0

Utknąłem w tym błędzie przez prawie 2 godziny:

Nieprawidłowo skonfigurowany w / api_users / users / 1 / nie można rozpoznać adresu URL dla relacji hiperłącza przy użyciu nazwy widoku „szczegóły-użytkowników”. Być może nie udało się uwzględnić powiązanego modelu w interfejsie API lub nieprawidłowo skonfigurowano pliklookup_field atrybut w tym polu.

Kiedy w końcu dostaję rozwiązanie, ale nie rozumiem dlaczego, więc mój kod to:

#models.py
class Users(models.Model):
    id          = models.AutoField(primary_key=True)
    name        = models.CharField(max_length=50, blank=False, null=False)
    email       = models.EmailField(null=False, blank=False) 
    class Meta:
        verbose_name = "Usuario"
        verbose_name_plural = "Usuarios"

    def __str__(self):
        return str(self.name)


#serializers.py
class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Users
        fields = (
            'id',
            'url',
            'name',        
            'email',       
            'description', 
            'active',      
            'age',         
            'some_date',   
            'timestamp',
            )
#views.py
class UserViewSet(viewsets.ModelViewSet):
    queryset = Users.objects.all()
    serializer_class = UserSerializer

#urls_api.py
router = routers.DefaultRouter()
router.register(r'users',UserViewSet, base_name='users')

urlpatterns = [ 
        url(r'^', include(router.urls)),
]

ale w moich głównych adresach URL było to:

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    #api users
    url(r'^api_users/', include('usersApi.users_urls', namespace='api')),

]

Aby w końcu rozwiązać problem kasowania przestrzeni nazw:

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    #api users
    url(r'^api_users/', include('usersApi.users_urls')),

]

I w końcu rozwiązuję swój problem, więc każdy może dać mi znać, dlaczego.

Cam T
źródło
0

Jeśli pominiesz pola „id” i „url” w swoim serializatorze, nie będziesz miał żadnego problemu. Możesz uzyskać dostęp do postów za pomocą identyfikatora, który i tak jest zwracany w obiekcie json, co sprawia, że ​​implementacja interfejsu użytkownika jest jeszcze łatwiejsza.

Eduardo A. Fernández Díaz
źródło
0

Miałem ten sam problem, myślę, że powinieneś sprawdzić

get_absolute_url

wartość wejściowa metody modelu obiektowego (** kwargs) title. i użyj dokładnej nazwy pola w lookup_field

hassanzadeh.sd
źródło