Django Rest Framework - Impossible de résoudre l'URL de la relation hypertexte à l'aide du nom de vue "user-detail"

108

Je construis un projet dans Django Rest Framework où les utilisateurs peuvent se connecter pour voir leur cave à vin. Mes ModelViewSets fonctionnaient très bien et tout d'un coup, j'obtiens cette erreur frustrante:

Impossible de résoudre l'URL de la relation hypertexte à l'aide du nom de vue "user-detail". Vous n'avez peut-être pas réussi à inclure le modèle associé dans votre API ou configuré incorrectement l' lookup_fieldattribut dans ce champ.

Le retraçage montre:

    [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.

J'ai un modèle d'utilisateur de messagerie personnalisé et le modèle de bouteille dans models.py est:

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

Mes sérialiseurs:

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')

Mes vues:

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

et enfin l'url:

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

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

Je n'ai pas de vue détaillée de l'utilisateur et je ne vois pas d'où ce problème pourrait provenir. Des idées?

Merci

bpipat
la source

Réponses:

96

Parce que c'est un HyperlinkedModelSerializervotre sérialiseur essaie de résoudre l'URL pour le associé Usersur votre Bottle.
Comme vous n'avez pas la vue détaillée de l'utilisateur, il ne peut pas le faire. D'où l'exception.

  1. Le simple fait d'enregistrer le UserViewSetavec le routeur ne résoudrait-il pas votre problème?
  2. Vous pouvez définir le champ utilisateur sur votre BottleSerializerpour utiliser explicitement le UserSerializerplutôt que d'essayer de résoudre l'URL. Consultez la documentation du sérialiseur sur le traitement des objets imbriqués pour cela .
Carlton Gibson
la source
1
Un grand merci, j'avais commenté le UserViewSet dans mes routeurs, cela l'a résolu!
bpipat
5
QUE LE POINT - faites-le explicitement - trop de magie est trop de temps perdu.
andilabs
Pouvez-vous indiquer ce qui est mal configuré sur mon projet ?
JJD
@ GrijeshChauhan - Merci! Maintenant corrigé.
Carlton Gibson
La raison pour laquelle cela ne fonctionnait pas était que django voulait afficher les données associées de User dans votre vue actuelle pour le paramètre User. Habituellement, il récupère une liste de valeurs disponibles. Étant donné que UserViewSet n'a pas été défini, il n'a pas été en mesure d'extraire les détails pour rendre la page Web. L'ajout d'UserViewSet et l'enregistrement sous le routeur par défaut rendent le rendu de tous les composants terminé.
Doogle
65

Je suis également tombé sur cette erreur et je l'ai résolue comme suit:

La raison en est que j'ai oublié de donner un espace de noms à "** - detail" (nom_vue, par exemple: détail utilisateur). Ainsi, Django Rest Framework n'a pas pu trouver cette vue.

Il y a une application dans mon projet, supposons que le nom de mon projet soit myprojectet que le nom de l'application soit myapp.

Il existe deux fichiers urls.py, l'un est myproject/urls.pyet l'autre est myapp/urls.py. Je donne à l'application un espace de noms myproject/urls.py, tout comme:

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

J'ai enregistré les routeurs du cadre de repos myapp/urls.py, puis j'ai obtenu cette erreur.

Ma solution était de fournir explicitement l'URL avec l'espace de noms:

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

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

Et cela a résolu mon problème.

Bovenson
la source
@boveson, cela fonctionne comme un charme! Merci d'avoir résolu des heures de frustration de mon côté.
lmiguelvargasf
Cela a également fonctionné pour moi. Un autre point important de mon côté était l'orthographe correcte du nom_base dans la Route!
maggie
1
La clé ici est le préfixe d'espace de noms empêche l'inversion de fonctionner .....
boatcoder
J'ai eu un problème comme celui-ci et cette réponse a résolu mon problème après 3 heures de recherche! @bovenson
Whale 52Hz
ou vous pouvez utiliser extra_kwargs comme le recommande la drf:extra_kwargs = {'url': {'view_name': 'myapp:user-detail'}}
ChrisRob
19

Peut-être que quelqu'un peut jeter un œil à ceci: http://www.django-rest-framework.org/api-guide/routers/

Si vous utilisez un espace de noms avec des sérialiseurs hyperliés, vous devrez également vous assurer que tous les paramètres view_name sur les sérialiseurs reflètent correctement l'espace de noms. Par exemple:

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

vous devez inclure un paramètre tel que view_name='api:user-detail'pour les champs de sérialiseur hyperliens vers la vue détaillée de l'utilisateur.

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

class Meta:
    model = User
    fields = ('url', 'username')
JackPy
la source
1
En résumé, donner à votre API un espace de noms provoque l'erreur dans le titre, vous ne voudrez probablement pas le faire à moins que vous ne vouliez le changer à de nombreux endroits.
Mark
travaillé pour moi! my urls.pyétait double imbriqué dans mon newsiteprojet: (1) newsite/urls.py(créé par django) (2) polls/urls.py(3) polls/api/v1/urls.py ............ Je dois mentionner le nom imbriqué en utilisanturl = serializers.HyperlinkedIdentityField(view_name="polls:polls_api:user-detail")
Grijesh Chauhan
12

Une autre mauvaise erreur qui cause cette erreur est d'avoir le nom_base inutilement défini dans votre urls.py. Par exemple:

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

Cela provoquera l'erreur indiquée ci-dessus. Sortez ce nom_base de là et revenez à une API fonctionnelle. Le code ci-dessous corrigera l'erreur. Hourra!

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

Cependant, vous n'avez probablement pas simplement ajouté arbitrairement le nom_base, vous l'avez peut-être fait parce que vous avez défini un def personnalisé get_queryset () pour la Vue et donc Django vous oblige à ajouter le nom_base. Dans ce cas, vous devrez définir explicitement l '' url 'comme HyperlinkedIdentityField pour le sérialiseur en question. Notez que nous définissons ce HyperlinkedIdentityField SUR LE SERIALIZER de la vue qui génère l'erreur. Si mon erreur était "Impossible de résoudre l'URL de la relation hypertexte à l'aide du nom de vue" étude-détail ". Vous n'avez peut-être pas réussi à inclure le modèle associé dans votre API ou configuré de manière incorrecte l' lookup_fieldattribut dans ce champ." Je pourrais résoudre ce problème avec le code suivant.

Mon ModelViewSet (le get_queryset personnalisé est la raison pour laquelle j'ai dû ajouter le nom_base au router.register () en premier lieu):

class StudyViewSet(viewsets.ModelViewSet):
    serializer_class = StudySerializer

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

Mon enregistrement de routeur pour ce ModelViewSet dans urls.py:

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

ET VOICI O WH EST L'ARGENT! Ensuite, je pourrais le résoudre comme ceci:

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

Oui. Vous devez définir explicitement ce HyperlinkedIdentityField sur lui-même pour qu'il fonctionne. Et vous devez vous assurer que le view_namedéfini sur le HyperlinkedIdentityField est le même que celui que vous avez défini sur le base_namedans urls.py avec un '-detail' ajouté après.

Colton Hicks
la source
2
Cela a fonctionné pour moi, mais j'ai dû mettre l'itinéraire complet <app_name>:studies-detail. Par exemple mon si mon application est appelée tanks, alors le chemin complet serait HyperlinkedIdentityField(view_name="tanks:studies-detail"). Pour comprendre cela, j'ai utilisé la commande django-exensions show_urls , pour voir l'itinéraire complet et l'étiquette que le routeur faisait automatiquement.
dtasev
10

Ce code devrait également fonctionner.

class BottleSerializer(serializers.HyperlinkedModelSerializer):

  user = UserSerializer()

  class Meta:
    model = Bottle
    fields = ('url', 'wine', 'user')
caglar
la source
3
A noter qui UserSerializerdoit être implémenté (il n'est pas prêt à importer), comme indiqué dans django-rest-framework.org/api-guide/serializers
Caumons
Cela a fonctionné pour moi, mais pour que cela fonctionne, j'ai dû changer router.register (r'bottles ', views.BottleViewSet, base_name =' bottle ') en router.register (r'bottles', views.BottleViewSet). Je ne sais pas pourquoi ce changement était nécessaire.
manpik du
4

J'ai rencontré cette erreur après avoir ajouté un espace de noms à mon URL

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

et en ajoutant app_name à mes urls.py

J'ai résolu ce problème en spécifiant NamespaceVersioning pour mon api de cadre de repos dans settings.py de mon projet

REST_FRAMEWORK = {
    'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.NamespaceVersioning'}
Kelechukwu Nwosu
la source
3

Aujourd'hui, j'ai eu la même erreur et les changements ci-dessous me sauvent.

Changement

class BottleSerializer(serializers.HyperlinkedModelSerializer):

à:

 class BottleSerializer(serializers.ModelSerializer):
Manish Pal
la source
2

Même erreur, mais raison différente:

Je définis un modèle utilisateur personnalisé, rien de nouveau champ:

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

Ceci est ma fonction de vue:

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

Comme je n'ai pas cédé querysetdirectement UserViewSet, je dois définir le base_namemoment où j'enregistre cet ensemble de vues. C'est là que mon message d'erreur causé par le urls.pyfichier:

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'

Vous avez besoin du base_namemême nom que votre modèle - customuser.

Belter
la source
Ancien message, mais votre commentaire "# <- base_name doit être 'customuser' au lieu de 'user'" est ce qui m'a sauvé la journée. Merci!
Hannon César
1

Si vous étendez les classes GenericViewSet et ListModelMixin et que vous rencontrez la même erreur lors de l'ajout du champ url dans la vue de liste, c'est parce que vous ne définissez pas la vue de détail. Assurez-vous d'étendre le mixage RetrieveModelMixin :

class UserViewSet (mixins.ListModelMixin,
                   mixins.RetrieveModelMixin,
                   viewsets.GenericViewSet):
Rowinson Gallego
la source
1

Il semble que HyperlinkedModelSerializerne sont pas d'accord avec le fait d'avoir un chemin namespace. Dans ma candidature, j'ai apporté deux modifications.

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

Dans le fichier URL importé

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

J'espère que cela t'aides.

Cody Wikman
la source
0

J'ai rencontré la même erreur en suivant le guide de démarrage rapide DRF http://www.django-rest-framework.org/tutorial/quickstart/ et en essayant de parcourir / users. J'ai fait cette configuration plusieurs fois auparavant sans problème.

Ma solution n'était pas dans le code mais dans le remplacement de la base de données.

La différence entre cette installation et les autres avant était lorsque j'ai créé la base de données locale.

Cette fois j'ai couru mon

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

immédiatement après avoir couru

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

Au lieu de l'ordre exact indiqué dans le guide.

Je soupçonnais que quelque chose n'était pas correctement créé dans la base de données. Je ne me souciais pas de ma base de données de développement, alors je l'ai supprimée et j'ai exécuté la ./manage.py migratecommande une fois de plus, j'ai créé un super utilisateur, j'ai consulté / users et l'erreur a disparu.

Quelque chose était problématique avec l'ordre des opérations dans lequel j'ai configuré DRF et la base de données.

Si vous utilisez sqlite et que vous êtes capable de tester le passage à une nouvelle base de données, cela vaut la peine d'essayer avant de disséquer tout votre code.

Ben Havilland
la source
0

Bouteille = sérialiseurs.PrimaryKeyRelatedField (read_only = True)

read_only vous permet de représenter le champ sans avoir à le lier à une autre vue du modèle.

Cristian Fernando
la source
0

J'ai eu cette erreur sur DRF 3.7.7 lorsqu'une valeur de slug était vide (égale à '') dans la base de données.

mrmuggles
la source
0

J'ai rencontré ce même problème et je l'ai résolu en l'ajoutant generics.RetrieveAPIViewcomme classe de base à mon ensemble de vues.

Jace Browning
la source
0

J'ai été coincé dans cette erreur pendant près de 2 heures:

ImproperlyConfigured at / api_users / users / 1 / Impossible de résoudre l'URL pour la relation hypertexte en utilisant le nom de vue "users-detail". Vous n'avez peut-être pas réussi à inclure le modèle associé dans votre API ou configuré incorrectement l' lookup_fieldattribut dans ce champ.

Quand j'obtiens enfin la solution mais que je ne comprends pas pourquoi, mon code est donc:

#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)),
]

mais dans mes URL principales, c'était:

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

]

Donc pour finalement résoudre le problème d'effacement de l'espace de noms:

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

]

Et je résous enfin mon problème, donc n'importe qui peut me dire pourquoi, mieux.

Cam T
la source
0

Si vous omettez les champs «id» et «url» de votre sérialiseur, vous n'aurez aucun problème. Vous pouvez accéder aux publications en utilisant de toute façon l'id retourné dans l'objet json, ce qui facilite encore plus l'implémentation de votre frontend.

Eduardo A. Fernández Díaz
la source
0

J'ai eu le même problème, je pense que vous devriez vérifier votre

get_absolute_url

valeur d'entrée de la méthode du modèle objet (** kwargs) title. et utilisez le nom de champ exact dans lookup_field

hassanzadeh.sd
la source