5
votes

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 )

3
I understand that what you want is to have a field in the User serializer pointing to the URL of its profile, right?dukebody
Are you trying to have url point to the user detail view or the user profile detail view?Jessamyn Smith
@JessamynSmith I am trying to get it to user-detail, that's why I have chosen the model as User and not UserProfilehemo
@dukebody yes, that's ithemo

3 Answers

9
votes
  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

1
votes

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)
0
votes

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