id field in django rest framework serializer
Asked Answered
R

4

30

I'm using django rest framework and just creating a simple serializer like this:

class PackageSerializer(serializers.HyperlinkedModelSerializer):

    class Meta:
        model = Package
        fields = ('id', 'url', 'title','location')

However I get this error:

KeyError at /cerberus/packages/
'id'

How come I can't get the primary key 'id' as part of my serialized data? Thanks for helping!

Retroflex answered 21/2, 2013 at 23:56 Comment(0)
B
67

HyperlinkedModelSerializer doesn't include the id by default. In 2.2 and earlier you'll need to add it explicitly as a field...

class PackageSerializer(serializers.HyperlinkedModelSerializer):
    id = serializers.Field()

    class Meta:
        model = Package
        fields = ('id', 'url', 'title','location')

From 2.3 onwards, you can simply add 'id' to the fields option...

class PackageSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Package
        fields = ('id', 'url', 'title','location')

From 3.x (< 3.3) onwards, you must use ReadOnlyField() instead of Field() if you want to add it explicitly and not use the fields option...

class PackageSerializer(serializers.HyperlinkedModelSerializer):
    id = serializers.ReadOnlyField()

    class Meta:
        model = Package
Bodice answered 22/2, 2013 at 8:59 Comment(5)
This tip helped me through a Tutorial of the rest_framework [link] (django-rest-framework.org/tutorial/…). In the tutorial there you have a line of pk = serializers.Field(), but it should be id = serializers.Field(). I oversaw that typo.Nabob
It should now be id = serializers.ReadOnlyField() for Django Rest Framework 3.xUsher
Thanks..Good explanation!!Ian
Creating a ModelSerializer without either the 'fields' attribute or the 'exclude' attribute has been deprecated since version 3.3.0. The third code example will therefore fail with an AssertionError.Deimos
And in the From "3.x (< 3.3)" Meta desc, what should be the fields: fields = ('id', 'url', 'title','location') or fields = '_ all _' ??And
L
18

According to the Django Rest Framework Documentation:

The default implicitly-generated id field is marked as read_only. This causes it to be removed on updates.

Thus, if you need the id field in an update method, you must not declare it as read-only but use a read-write field instead:

class PackageSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField()
Leverett answered 8/12, 2019 at 22:27 Comment(4)
You should not have to declare the pk on the serialization modelMargarine
@Margarine you do if you want to be able to create an instance with a specific id rather than an auto idGangland
I can't think of a circumstance in which you'd override the model's id with something else for serializationMargarine
Making a 64-bit id into a string to be valid Javascript, like Facebook does. (See developers.facebook.com/docs/workplace/reference/graph-api). Base64 URL encoding an id, like Instagram probably does.Sogdian
F
5

I just tweaked this to make it a little more pluggable by creating this class:

class HyperlinkedModelSerializerWithId(serializers.HyperlinkedModelSerializer):
    """Extend the HyperlinkedModelSerializer to add IDs as well for the best of
    both worlds.
    """
    id = serializers.ReadOnlyField()

Then, just use that to make your serializer and you're on your way.

Footpoundal answered 31/1, 2018 at 21:51 Comment(1)
Very Elegant. Thank you!!Kelm
C
0

Background

As of Django Rest Framework 3.14.0 (I'm using it right now), it would seem that none of the solutions provided above work. (Maybe romor's, but this response goes into much more details, IMO)

Input

I have a BaseModel that has Meta property "abstract" set to True. Then I'm creating a serializer for my non-base model.

Some sample code — Base Class:

# _base.py
from uuid import uuid4

from django.db import models

class BaseModel(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
    created_at = models.DateTimeField(auto_now_add=True...)
    updated_at = models.DateTimeField(auto_now=True...)

    class Meta:
        abstract = True

Model I'm making a serializer for:

from django.db import models

from ._base import BaseModel


class Comment(BaseModel):
    file = models.FileField(...)
    text = models.TextField(...)

    author = models.ForeignKey("accounts.BaseUser"...)

    class Meta:
        verbose_name = _("Comment")
        verbose_name_plural = _("Comments")
        ordering = ["-created_at"]

As you can see, I have a field called id, an instance of UUIDField class.

Using a read-only field results in that id field being omitted from the response, even though it's present in the Model definition. I've tried this field as an option of UUIDField, and using ReadOnlyField directly.

Solution

To fix this, I've created a serializer ModelWithUUID, it looks something like this:

from rest_framework import serializers


class ModelWithUUID(serializers.ModelSerializer):
    id = serializers.UUIDField()

So, in a way, this is what romor and Jann (the editor) suggested in their response.

By doing this, my final serializer is pretty simple, and looks like this:

class CommentSerializer(ModelWithUUID):
    author = AdditionalSerializerForAuthor()  # Not the actual name
    file = FileRepresentationField(allow_null=True)  # I need some "weird" conversions, don't judge me

    class Meta:
        model = Comment
        fields = "__all__"

This allows me to use __all__ for fields and have an id field at all times.

Additional background, that you may need to know to understand why I do things this way (you can skip this)

It should be noted that I use Django 5.0.1 and drf_yasg.

Maybe my approach is wrong, but I use the serializer's .data property as data for response. And to be even more specific — I use a combination of 3 serializers for all (or almost all) of my endpoints:

  • Input Serialzier, that is used as a type hint for drf_yasg;
  • Output Serializer, same (CommentSerializer is a part of one);
  • Internal Seraializer, that takes data from Input Serializer, and returns an Output Serializer, or a Validation Error.

This combination seems to be pretty flexible so far, but it may not be the best practice in the long run.

Ceiba answered 29/1 at 19:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.