Christian Torres
Christian Torres

Reputation: 107

DRF, add custom field to ModelSerializer

I have some models in my project and I need a especial response of the API, i'm using Django Rest framework.

class Goal(models.Model):
    name = models.CharField()
    # more fields

class Task(models.Model):
    name = models.CharField()
    goal = models.ForeignKey(Goal)

class UserTask(models.Model):
    goal = models.ForeignKey(Goal)
    user = models.ForeignKey(User)
    # other fields

I have this response:

{
  "name": "One goal",
  "task": [
    {
      "name": "first task"
    },
    {
      "name": "second tas"
    }
  ]
}

But I need this:

{
  "name": "One goal",
  "task": [
    {
      "name": "first task",
      "is_in_usertask": true
    },
    {
      "name": "second tas",
      "is_in_usertask": false
    }
  ]
}

I saw this in DRF docs but I don't know how to filter UserTask by the current user (or other that is given in URL paramenter) and each Goal.

Edit:

# serializers

class TaskSerializer(serializers.ModelSerializer):
    class Meta:
        model = Task


class GoalSerializer(serializers.ModelSerializer):
    # related_name works fine
    tasks = TaskSerializer(many=True)

    class Meta:
        model = Goal

Upvotes: 4

Views: 8500

Answers (3)

pymen
pymen

Reputation: 6539

You can use VirtualFieldSerializer which is similar to SerializerMethodField

from rest_framework.fields import Field

class VirtualFieldSerializer(Field):
    """Virtual serializer field for including the serialized representation of another serializer's data.
    This field is read-only, and gives flexibility in constructing complex serializer structures. """

    def __init__(self, serializer, **kwargs):
        self.serializer = serializer
        kwargs['source'] = '*'
        kwargs['read_only'] = True
        super().__init__(**kwargs)

    def to_representation(self, value):
        return self.serializer().to_representation(value)

Example

if You have Contact model, and SmsChannel model which have OneToOne relation to Contact you can get nested result like {'channels': {'sms': {'status': 1, 'number': '1234'}}

class ContactSerializer(serializers.ModelSerializer):
    channels = VirtualFieldSerializer(ContactChannelsSerializer)

class ContactSmsChannelSerializer(serializers.ModelSerializer):
    class Meta:
        model = SmsChannel
        fields = ('number', 'status')

class ContactChannelsSerializer(serializers.Serializer):
    sms = ContactSmsChannelSerializer(source='sms_channel')

Upvotes: 0

JPG
JPG

Reputation: 88429

try to use SerializerMethodField field as

class TaskSerializer(serializers.ModelSerializer):
    is_in_usertask = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = Task
        fields = ('name', 'is_in_usertask')

    def get_is_in_usertask(self, task):
        return UserTask.objects.filter(user=self.context['request'].user, goal=task.goal).exists()

Upvotes: 6

Pavel Minenkov
Pavel Minenkov

Reputation: 403

Take a look to this conversation: How to get Request.User in Django-Rest-Framework serializer?

You can't access to request.user directly

Upvotes: 0

Related Questions