1

My member app expects the JSON response to contain a primary key result for the model ID. Rather than use integer IDs, I want to use slugs. I know that in the standard django framework you can do something like this:

from rest_framework import serializers
from .models import Page

class PageSerializer(serializers.ModelSerializer):
   id = serializers.CharField(source='slug')

   class Meta:
      model = Page
      fields = ('name','id',...etc )

However, this doesn't work when using the json API - in my response I still have something like

"data": [{
        "type": "pages",
        "id": "2",
        "attributes": {
            "name": "Some page name",                 
        }]

When I want the "id" field to be something like "some-page-name" (the slug)

Is this not possible with the json API. For clarity the equivalent json API import above would be

 from rest_framework_json_api import serializers

Many thanks

**** addition ****

To help clarify the problem I am facing, here is a serializer using the standard REST framework. All attributes shown below are included in the Page model.

from rest_framework import serializers
from .models import Page

class PageSerializer(serializers.ModelSerializer):
    id = serializers.CharField(source='slug')
    class Meta:
        model = Page
        fields = ('name','id')

The JSON response I get at http://localhost:8000/api/pages is as follows

 [
    {
      "name": "Page 1",
      "id": "page-1"
   },
   {
    "name": "Page 2",
    "id": "page-2"
   },
   {
     etc
   }
 ]

When I use the json API, doing the exactly the same, but obviously importing

from rest_framework_json_api import serializers 

I am not able to change the id value in the same way, I have something like this when viewing http://localhost:8000/api/pages

{
   "links": {
      omitted for brevity ...
   },
  "data": [
      {
        "type": "pages",
        "id": "1",
        "attributes": {
            "name": "Page 1"
        }
     },
     {
        "type": "pages",
        "id": "2",
        "attributes": {
            "name": "Page 2"
       }
    },
]
8
  • don't include 'id' in fields. Commented Feb 18, 2019 at 17:05
  • 1
    do you have the field slug in your model? Commented Feb 18, 2019 at 17:05
  • If your model has a slug field, why not just pass that as field (and give it the label "id")? Commented Feb 18, 2019 at 17:08
  • Hi - thanks for getting back. If I remove 'id' in fields it makes no difference, and sorry, yes I do have slug in the model .. I didnt show it in the fields list for brevity Commented Feb 18, 2019 at 17:08
  • @dirkgroten sorry can you clarify? Commented Feb 18, 2019 at 17:10

2 Answers 2

2

This is the solution which allows me to view responses at eg http://localhost:8000/api/pages and make a request to http://localhost:8000/api/pages/some-slug in a format that Emberjs (expecting the response to conform with the JSON API spec) won't complain about.

In my serializer (PageSerializer.py):

from rest_framework_json_api import serializers
from .models import Page

class PageSerializer(serializers.ModelSerializer):

    class Meta:
        model = Page
        fields = ('name','id','url','slug')
        lookup_field = 'slug'

        // if you want to show the links field in the response
        extra_kwargs = {'url': {'lookup_field':'slug'}}

In the view (PageViewSet)

from rest_framework import viewsets
from .models import Page
from .serializers import PageSerializer

class PageViewSet(viewsets.ModelViewSet):

    queryset = Page.objects.all().order_by('name')
    serializer_class = PageSerializer
    lookup_field = 'slug'

The router stays unchanged:

from django.urls import path, include
from rest_framework import routers
from page import views

router = routers.DefaultRouter(trailing_slash=False)
router.register('pages',views.PageViewSet)

urlpatterns = [
   path('api/',include(router.urls)),
]
Sign up to request clarification or add additional context in comments.

Comments

0

I think what you want is to use your slug field instead of id as your primary key so that you can pass that parameter in your url to get the desired result. You can do something like this in your url

url(r'^something-here/(?P<slug>[\w-]+)/$', "your_view_name_here", name='name_you_want')

In your views then you can do something like the following:

class YourListAPIView(ListAPIView):
    serializer_class = YourSerializer
    def get_queryset(self):
        slug = self.kwargs['slug']
        return ModelName.objects.filter(slug=slug)

8 Comments

Sorry for not explaining this more clearly - I was hoping by giving the example from the standard REST framework in my original question that there would be a simple way to achieve the same thing using the json api. In the json response (shown in the original question), the "id" field needs to show the slug. I don't think this achieves that but apologies if I'm mistaken
I think you are thinking in the wrong way. Your end result is to use id as the parameter in the url so that you can pass the slug in the url and get a response. Right?
Hi. Yes, I would want to be able to use the slug to obtain the instance response eg localhost:8000/api/pages/some-page versus localhost:8000/api/pages/1 but when viewing the response for all pages at localhost:8000/api/pages, that response needs to include the slug value for the id, not the integer - please see amended question above. I can achieve this using the "standard" DRF (see amended question) but not getting the same result when I use the rest_framework_json_api, which is what I want.
@Newfoundland id is a default field and a default primary key. As you said you already have slug in your model field. Use that field as your url parameter. Change your url as mentioned above in my answer so that it accepts the slug when you are going to call the API url. After that just use the slug and do something in your view.
Sorry but can't get this to work. If I use the generics.ListAPIView in my view, utilising the PageSerializer class which consumes the serializers.ModelSerializer from rest_framework_json_api, I get this error AssertionError: basename argument not specified, and could not automatically determine the name from the viewset, as it does not have a .queryset attribute. Don't worry I'll just change the primary key in my Django models to be the slug.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.