drf-forms/drf-schema-adapter

Using HyperLinkedModelSerializer with Endpoint

Opened this issue · 0 comments

I'm trying to get an Endpoint working that utilizes DRFs HyperLinkedModelSerializer but haven't quite got it right. Not sure if it's a bug, expected behavior, incorrect docs or incorrect reading of said docs. Anyway...

I have a very basic model that looks like this:

   class Project(models.Model):
        title = models.CharField(max_length=255)

Attempt 1 - Failed

From the docs, it looks like it should be relatively simple so I tried:

class TestEndpoint(Endpoint):
    model = Test
    base_serializer = serializers.HyperlinkedModelSerializer

router = EndpointRouter()

router.register(endpoint=TestEndpoint)

urlpatterns = [
    path("", include(router.urls)),
]

...but only the standard (non-hyperlinked) response is returned.

{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "id": 1,
      "name": "This is a test object",
      "__str__": "Test object (1)"
    }
  ]
}

Attempt 2 - Failed

Maybe it works if I create a custom serializer and apply it directly to the Endpoint:

class TestSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Test
        fields = "__all__"

class TestEndpoint(Endpoint):
    model = Test
    serializer = TestSerializer

but the result was the exact same as the first :(

Attempt 3 - Failed

I noticed that in the app code, you tend to instantiate endpoints before registering them with the router (the docs suggest otherwise). So I tried this myself.

router.register(endpoint=TestEndpoint())

This fails in a different way. Now an ImproperlyConfigured is raised by DRF:

Could not resolve URL for hyperlinked relationship using view name "test-detail". You may have failed to include the related model in your API, or incorrectly configured the `lookup_field` attribute on this field.

This is because the url field which is autogenerated by the HyperLinkedModelSerializer can't find the detail view. It does exist, but examining the URL conf show that it exists under the name <application_name>/test-detail rather than simply test-detail.

Attempt 4 - Success (sort of)

So what seemed to work was starting from Attempt 3 but explicitly defining the url:

class TestEndpoint(Endpoint):
    model = Test
    serializer = TestSerializer
    url = 'test'

which results in the expected output. Note though that the url must equal the lower case model name.

{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "url": "http://ghfdb.localhost:8000/api/v1/test/1/",
      "name": "This is a test object"
    }
  ]
}

Conclusion

I don't know if I misinterpreted the docs but I really thought my first attempt with base_serializer should just work. Explicitly defining a serializer and url for each endpoint seems to violate the DRY principles of this package so I wanted to see if there is something I'm missing with my approach.