django-json-api/django-rest-framework-json-api

Related name is not being passed to `object-related` view in `HyperlinkedModelSerializer` when using `ResourceRelatedField`

Nekidev opened this issue · 4 comments

Description of the Bug Report

When using HyperlinkedModelSerializer, the related_link_view_name view is not getting passed the related_name argument.

Here is the serializer definition:

class ImageSerializer(serializers.HyperlinkedModelSerializer):
    """
    Converts an Image object into JSON:API data.
    """

    included_serializers = {
        "categories": CategorySerializer
    }

    class Meta:
        model = Image
        fields = [
            "categories",
            "url"
        ]

    class JSONAPIMeta:
        included_resources = ["categories"]

    categories = relations.ResourceRelatedField(
        queryset = Category.objects,
        many=True,
        related_link_view_name='image-related',
        related_link_url_kwarg='image_pk',
        self_link_view_name='image-relationships'
    )

And urls.py:

from django.urls import path

from .views import ImagesViewSet, ImageRelationshipsView

urlpatterns = [
    path("image", ImagesViewSet.as_view({"get": "list"}), name="image"),
    path(
        "image/<uuid:pk>",
        ImagesViewSet.as_view({"get": "retrieve"}),
        name="image-detail",
    ),
    path(
        "image/<uuid:image_pk>/<related_field>",
        ImagesViewSet.as_view({"get": "retrieve_related"}),
        name="image-related",
    ),
    path(
        "image/<uuid:pk>/relationships/<related_field>",
        ImageRelationshipsView.as_view(),
        name="image-relationships",
    ),
]

In the third path, there is an exception being raised:

django.urls.exceptions.NoReverseMatch: Reverse for 'image-related' with keyword arguments '{'image_pk': UUID('cd7ce8fd-ac01-4dd3-aa10-5f6344494ad0')}' not found. 1 pattern(s) tried: ['v2/image/(?P<image_pk>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/(?P<related_field>[^/]+)\\Z']

In the fourth path, the related_link_url_kwarg argument is not being used. The path still resolves to the default pk path parameter.

Checklist

  • Certain that this is a bug (if unsure or you have a question use discussions instead)
  • Code snippet or unit test added to reproduce bug

Thanks for the report. For better understanding of the issue, do you think you could create a reproducing example in our example app? It could just be a PR with the changes of the example app. That would be very helpful.

I believe these lines are at fault:

# Assuming RelatedField will be declared in two ways:
# 1. url(r'^authors/(?P<pk>[^/.]+)/(?P<related_field>\w+)/$',
# AuthorViewSet.as_view({'get': 'retrieve_related'}))
# 2. url(r'^authors/(?P<author_pk>[^/.]+)/bio/$',
# AuthorBioViewSet.as_view({'get': 'retrieve'}))
# So, if related_link_url_kwarg == 'pk' it adds 'related_field' parameter to reverse()
if self.related_link_url_kwarg == "pk":
related_kwargs = self_kwargs
else:
related_kwargs = {
self.related_link_url_kwarg: kwargs[self.related_link_lookup_field]
}

A temporary workaround is to specify related_link_url_kwarg='pk' in your serializer field:

categories = relations.ResourceRelatedField(
    queryset=Category.objects.all(),
    many=True,
    related_link_view_name='image-related',
    related_link_url_kwarg='pk',
    related_link_lookup_field='image_pk',
    self_link_view_name='image-relationships',
)

This tricks it into including the related_field in the url kwargs, but you are still free to name your url kwarg the same as the lookup field, e.g. /<str:name>/relationships/<str:related_field>

I had a closer look at this. This is an DJA feature of related link and the related_link_url_kwarg needs to be set to pk for it to work, as retrieve_related expects it this way unless the logic is overwritten in your view. So if you rename image_pk to 'pk` in the path it should work as desgined.

See more detailed documentation here. In case it still does not, it will be important that you show what kind of view you are using.

I assume as there has been no feedback that this has been resolved by following the documentation as explained above. If this is not the case, please comment.