6

Problem :

I am getting an error like this .

ImproperlyConfigured at /api/users/

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.

I read this post but it didn't work.

serializers.py

class UserSerializer(serializers.ModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name='user-detail',
                                               lookup_field='profile')

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


class UserProfileSerializer(serializers.ModelSerializer):
    user = serializers.ReadOnlyField(source='user.username')

    class Meta:
        model = UserProfile
        fields = "__all__"
        # user = serializers.ReadOnlyField(source='owner.username')

    def create(self, validated_data):
        pass

    def update(self, instance, validated_data):
        pass

urls.py

user_profile_list = UserProfileViewSet.as_view({
    'get': 'list',
    'post': 'create'
})
user_profile_detail = UserProfileViewSet.as_view({
    'get': 'retrieve',
    'put': 'update',
    'patch': 'partial_update',
    'delete': 'destroy'
})
user_list = UserViewSet.as_view({
    'get': 'list'
})
user_detail = UserViewSet.as_view({
    'get': 'retrieve'
})
user_profiles_detail = UserViewSet.as_view({
    'get': 'profile'
})

router = DefaultRouter()
router.register(r'userprofiles', views.UserProfileViewSet)
router.register(r'users', views.UserViewSet)

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

views.py

class UserProfileViewSet(viewsets.ModelViewSet):
    """
    This viewset automatically provides `list`, `create`, `retrieve`,
    `update` and `destroy` actions.
    """
    queryset = UserProfile.objects.all()
    serializer_class = UserProfileSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,
                          IsOwnerOrReadOnly,)
    pagination_class = LimitTenPagination

    @detail_route(renderer_classes=[renderers.JSONRenderer])
    def perform_create(self, serializer):
        serializer.save(user=self.request.user)


class UserViewSet(viewsets.ReadOnlyModelViewSet):
    """
    This viewset automatically provides `list` and `detail` actions
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer

Snippet from my models.py

class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')

What I tried :

I tried changing user-detail to api:user-detail ( yes api namespace do exist in the main urls.py file )

hemo
  • 110
  • 1
  • 10

3 Answers3

9
  1. Looking at the docs, I believe you have to use HyperLinkedRelatedField. Also, see this related SO post.

  2. You are confusing arguments in your serializer field definition. What I believe should be:

    class UserSerializer(serializers.ModelSerializer):
        url = serializers.HyperlinkedRelatedField(view_name='api:userprofile-detail',
                                                  source='profile')
    

Edit :

Added namespace api

hemo
  • 110
  • 1
  • 10
dukebody
  • 7,025
  • 3
  • 36
  • 61
  • I found that this worked only if I named the field the same as in the model, e.g.: profile = serializers.HyperlinkedRelatedField(read_only=True, view_name='userprofiles-detail') – Jessamyn Smith Jan 20 '18 at 21:56
  • 1
    @JessamynSmith yes that's why namespace needs to be added – hemo Jan 20 '18 at 22:02
1

Remove lookup_filed attribute from your HyperlinkedIdentityField since you do not need it.

Hence your serializer class should instead look like below:

class UserSerializer(serializers.ModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name='user-detail', source='profile',)

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

The attribute lookup_filed is only needed when referring to a model class with relationship-field/attribute used as a primary key instead of the default autoincrement. For example, it would have been relevant to use it if your model class looked like below:

class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile', primary_key=True)
Mitch
  • 1,556
  • 1
  • 17
  • 17
0

If you're extending the GenericViewSet and ListModelMixin classes, and have the same error when adding the url field in the list view, it's because you're not defining the detail view. Be sure you're extending the RetrieveModelMixin mixin:

class UserViewSet (mixins.ListModelMixin,
                   mixins.RetrieveModelMixin,
                   viewsets.GenericViewSet):

Credits to an answer from Rowinson Gallego

Bonfix Ngetich
  • 1,087
  • 11
  • 15