Django Rest Framework - não foi possível resolver a URL para relacionamento com hiperlink usando o nome de visualização “detalhe do usuário”

108

Estou construindo um projeto no Django Rest Framework onde os usuários podem fazer o login para visualizar sua adega. Meus ModelViewSets estavam funcionando bem e, de repente, recebo este erro frustrante:

Não foi possível resolver o URL para relacionamento com hiperlink usando o nome de exibição "detalhes do usuário". Você pode ter falhado ao incluir o modelo relacionado em sua API ou configurado incorretamente o lookup_fieldatributo neste campo.

O traceback mostra:

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

Eu tenho um modelo de usuário de e-mail personalizado e o modelo de garrafa em models.py é:

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

Meus serializadores:

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

Minhas opiniões:

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

e finalmente o url:

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

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

Não tenho uma visualização de detalhes do usuário e não vejo de onde esse problema pode vir. Alguma ideia?

obrigado

bpipat
fonte

Respostas:

96

Por ser um HyperlinkedModelSerializerserializador, ele está tentando resolver a URL do relacionado Userem seu Bottle.
Como você não tem a visualização de detalhes do usuário, ele não pode fazer isso. Daí a exceção.

  1. Não seria apenas registrar o UserViewSetcom o roteador resolver seu problema?
  2. Você pode definir o campo do usuário em seu BottleSerializerpara usar explicitamente o em UserSerializervez de tentar resolver o URL. Veja a documentação do serializador sobre como lidar com objetos aninhados para isso .
Carlton Gibson
fonte
1
Muito obrigado, eu tinha comentado o UserViewSet em meus roteadores, isso resolveu o problema!
bpipat
5
ESSE É O PONTO ---- faça-o explicitamente --- muita magia é muito tempo perdido.
andilabs
Você pode apontar o que está mal configurado no meu projeto ?
JJD
@ GrijeshChauhan— Obrigado! Agora corrigido.
Carlton Gibson
O motivo pelo qual não estava funcionando era porque django queria mostrar os dados relacionados do usuário em sua visão atual para o parâmetro Usuário. Normalmente, ele pega uma lista de valores disponíveis. Como UserViewSet não foi definido, não foi possível extrair os detalhes para renderizar a página da web. Adicionar UserViewSet e registrar sob o roteador padrão torna tudo completo para renderizar todos os componentes.
Doogle
65

Também encontrei este erro e resolvi-o da seguinte forma:

O motivo é que esqueci de dar a "** - detalhe" (view_name, por exemplo: detalhe do usuário) um namespace. Portanto, o Django Rest Framework não conseguiu encontrar essa visão.

Há um aplicativo em meu projeto, suponha que o nome do meu projeto seja myprojecte o nome do aplicativo seja myapp.

Existem dois arquivos urls.py, um é myproject/urls.pye o outro é myapp/urls.py. Eu atribuo ao aplicativo um namespace em myproject/urls.py, assim como:

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

Registrei os demais roteadores do framework myapp/urls.pye recebi este erro.

Minha solução foi fornecer o url com o namespace explicitamente:

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

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

E resolveu meu problema.

Bovenson
fonte
@boveson, isso funciona como um encanto! Obrigado, você resolveu horas de frustração do meu lado.
lmiguelvargasf
Isso também funcionou para mim. Um ponto também importante da minha parte foi a grafia correta de base_name na Rota!
maggie
1
A chave aqui é que o prefixo do namespace evita que o reverso funcione ...
boatcoder
Tive um problema como este e esta resposta resolveu o meu problema após 3 horas de pesquisa! @bovenson
Whale 52Hz
ou você pode usar extra_kwargs como drf recomenda:extra_kwargs = {'url': {'view_name': 'myapp:user-detail'}}
ChrisRob
19

Talvez alguém possa dar uma olhada nisso: http://www.django-rest-framework.org/api-guide/routers/

Se estiver usando namespacing com serializadores com hiperlink, você também precisará garantir que quaisquer parâmetros view_name nos serializadores reflitam corretamente o namespace. Por exemplo:

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

você precisaria incluir um parâmetro como view_name='api:user-detail'para campos do serializador com hiperlink para a visualização de detalhes do usuário.

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

class Meta:
    model = User
    fields = ('url', 'username')
JackPy
fonte
1
Resumindo, dar à sua api um namespace causa o erro no título, você provavelmente não deseja fazer isso, a menos que queira alterá-lo em muitos lugares.
Marcos
funcionou para mim! my urls.pyfoi duplamente aninhado em meu newsiteprojeto: (1) newsite/urls.py(criado por django) (2) polls/urls.py(3) polls/api/v1/urls.py ............ Tenho que mencionar o nome aninhado usandourl = serializers.HyperlinkedIdentityField(view_name="polls:polls_api:user-detail")
Grijesh Chauhan
12

Outro erro desagradável que causa esse erro é definir o base_name desnecessariamente no seu urls.py. Por exemplo:

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

Isso causará o erro observado acima. Tire esse base_name de lá e volte para uma API funcional. O código a seguir corrigirá o erro. Hooray!

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

No entanto, você provavelmente não adicionou apenas base_name arbitrariamente, você pode ter feito isso porque definiu um def get_queryset () personalizado para a Visualização e, portanto, o Django ordena que você adicione o base_name. Nesse caso, você precisará definir explicitamente o 'url' como um HyperlinkedIdentityField para o serializador em questão. Observe que estamos definindo este HyperlinkedIdentityField NO SERIALIZER da view que está gerando o erro. Se meu erro foi "Não foi possível resolver o URL para relacionamento com hiperlink usando o nome da visualização" detalhe do estudo ". Você pode ter falhado ao incluir o modelo relacionado em sua API ou configurado incorretamente o lookup_fieldatributo neste campo." Eu poderia consertar isso com o seguinte código.

Meu ModelViewSet (o get_queryset personalizado é o motivo pelo qual tive que adicionar o base_name ao router.register () em primeiro lugar):

class StudyViewSet(viewsets.ModelViewSet):
    serializer_class = StudySerializer

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

Meu registro de roteador para este ModelViewSet em urls.py:

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

E AQUI ESTÁ ONDE O DINHEIRO ESTÁ! Então eu poderia resolver assim:

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

Sim. Você deve definir explicitamente este HyperlinkedIdentityField em si mesmo para que funcione. E você precisa ter certeza de que o view_namedefinido em HyperlinkedIdentityField é o mesmo que você definiu base_nameem urls.py com um '-detail' adicionado depois dele.

Colton Hicks
fonte
2
Isso funcionou para mim, porém tive que colocar o caminho completo <app_name>:studies-detail. Por exemplo, meu, se meu aplicativo for chamado tanks, o caminho completo será HyperlinkedIdentityField(view_name="tanks:studies-detail"). Para descobrir isso, usei o comando django-exensions show_urls , para ver a rota completa e o rótulo que o roteador estava fazendo automaticamente.
dtasev
10

Este código deve funcionar também.

class BottleSerializer(serializers.HyperlinkedModelSerializer):

  user = UserSerializer()

  class Meta:
    model = Bottle
    fields = ('url', 'wine', 'user')
caglar
fonte
3
Vale a pena notar que UserSerializerdeve ser implementado (não está pronto para importar), conforme mostrado em django-rest-framework.org/api-guide/serializers
Caumons
Isso funcionou para mim, mas para que funcionasse, tive que alterar router.register (r'bottles ', views.BottleViewSet, base_name =' bottles ') para router.register (r'bottles', views.BottleViewSet). Não sei por que essa mudança foi necessária.
manpikin
4

Encontrei este erro depois de adicionar um namespace ao meu url

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

e adicionando app_name ao meu urls.py

Resolvi isso especificando NamespaceVersioning para minha API de estrutura restante em settings.py de meu projeto

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

Hoje, recebi o mesmo erro e as alterações abaixo me resgatam.

mudança

class BottleSerializer(serializers.HyperlinkedModelSerializer):

para:

 class BottleSerializer(serializers.ModelSerializer):
Manish Pal
fonte
2

Mesmo erro, mas motivo diferente:

Eu defino um modelo de usuário personalizado, nenhum campo novo:

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

Esta é a função da minha visão:

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

Como não cedi querysetdiretamente UserViewSet, tenho que definir base_namequando registrar este conjunto de visualizações. É aqui que minha mensagem de erro causada por urls.pyarquivo:

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'

Você precisa do base_namemesmo nome do seu modelo - customuser.

Belter
fonte
Postagem antiga, mas seu comentário "# <- base_name precisa ser 'customuser' em vez de 'usuário'" é o que salvou meu dia. Obrigado!
Hannon César
1

Se você está estendendo as GenericViewSet e ListModelMixin aulas, e têm o mesmo erro ao adicionar o url campo na vista de lista, é porque você não está definindo a vista de detalhe. Certifique-se de estender o mixin RetrieveModelMixin :

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

Parece que HyperlinkedModelSerializernão concordo em ter um caminho namespace. Em meu aplicativo, fiz duas alterações.

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

No arquivo de urls importado

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

Espero que isto ajude.

Cody Wikman
fonte
0

Encontrei o mesmo erro enquanto seguia o guia de início rápido do DRF http://www.django-rest-framework.org/tutorial/quickstart/ e, em seguida, tentava navegar para / users. Já fiz essa configuração muitas vezes antes, sem problemas.

Minha solução não estava no código, mas em substituir o banco de dados.

A diferença entre esta instalação e as outras anteriores foi quando criei o banco de dados local.

Desta vez eu corri meu

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

imediatamente depois de correr

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

Em vez da ordem exata listada no guia.

Suspeitei que algo não foi criado corretamente no DB. Não me importei com meu dev db, então o apaguei e executei o ./manage.py migratecomando mais uma vez, criei um superusuário, naveguei até / users e o erro desapareceu.

Algo estava problemático com a ordem de operações em que configurei o DRF e o db.

Se você estiver usando sqlite e for capaz de testar a mudança para um novo banco de dados, vale a pena tentar antes de dissecar todo o seu código.

Ben Havilland
fonte
0

Bottle = serializers.PrimaryKeyRelatedField (read_only = True)

read_only permite que você represente o campo sem ter que vinculá-lo a outra visualização do modelo.

Cristian fernando
fonte
0

Recebi aquele erro no DRF 3.7.7 quando um valor de slug estava vazio (igual a '') no banco de dados.

mrmuggles
fonte
0

Eu encontrei esse mesmo problema e resolvi adicionando generics.RetrieveAPIViewcomo uma classe base ao meu conjunto de visualizações.

Jace Browning
fonte
0

Fiquei preso neste erro por quase 2 horas:

ImproperlyConfigured at / api_users / users / 1 / Não foi possível resolver URL para relacionamento com hiperlink usando o nome de exibição "detalhes do usuário". Você pode ter falhado ao incluir o modelo relacionado em sua API ou configurado incorretamente o lookup_fieldatributo neste campo.

Quando finalmente consigo a solução, mas não entendo o porquê, meu código é:

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

mas em meus URLs principais, era:

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

]

Então, para finalmente resolver o problema ao apagar o namespace:

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

]

E eu finalmente resolvo meu problema, para que qualquer um possa me dizer o porquê, melhor.

Cam T
fonte
0

Se você omitir os campos 'id' e 'url' do serializador, não terá problemas. Você pode acessar as postagens usando o id que é retornado no objeto json de qualquer maneira, o que torna ainda mais fácil implementar seu frontend.

Eduardo A. Fernández Díaz
fonte
0

Eu tive o mesmo problema, acho que você deve verificar o seu

get_absolute_url

título do valor de entrada do método do modelo de objeto (** kwargs). e use o nome de campo exato em lookup_field

hassanzadeh.sd
fonte