How can one customize Django Rest Framework serial

2019-03-26 08:44发布

问题:

I have a Django model that is like this:

class WindowsMacAddress(models.Model):
    address = models.TextField(unique=True)
    mapping = models.ForeignKey('imaging.WindowsMapping', related_name='macAddresses')

And two serializers, defined as:

class WindowsFlatMacAddressSerializer(serializers.Serializer):
    address = serializers.Field()

class WindowsCompleteMappingSerializer(serializers.Serializer):
    id = serializers.Field()
    macAddresses = WindowsFlatMacAddressSerializer(many=True)
    clientId = serializers.Field()

When accessing the serializer over a view, I get the following output:

[
    {
        "id": 1, 
        "macAddresses": [
            {
                "address": "aa:aa:aa:aa:aa:aa"
            }, 
            {
                "address": "bb:bb:bb:bb:bb:bb"
            }
        ], 
        "clientId": null
    }
]

Almost good, except that I'd prefer to have:

[
    {
        "id": 1, 
        "macAddresses": [
            "aa:aa:aa:aa:aa:aa",
            "bb:bb:bb:bb:bb:bb"
        ],
        "clientId": null
    }
]

How can I achieve that ?

回答1:

Create a custom serializer field and implement to_native so that it returns the list you want.

If you use the source="*" technique then something like this might work:

class CustomField(Field):
    def to_native(self, obj):
        return obj.macAddresses.all()

I hope that helps.

Update for djangorestframework>=3.9.1

According to documentation, now you need override either one or both of the to_representation() and to_internal_value() methods. Example

class CustomField(Field):
    def to_representation(self, value)
        return {'id': value.id, 'name': value.name}


回答2:

Carlton's answer will work do the job just fine. There's also a couple of other approaches you could take.

You can also use SlugRelatedField, which represents the relationship, using a given field on the target.

So for example...

class WindowsCompleteMappingSerializer(serializers.Serializer):
    id = serializers.Field()
    macAddresses = serializers.SlugRelatedField(slug_field='address', many=True, read_only=True)
    clientId = serializers.Field()

Alternatively, if the __str__ of the WindowsMacAddress simply displays the address, then you could simply use RelatedField, which is a basic read-only field that will give you a simple string representation of the relationship target.

# models.py
class WindowsMacAddress(models.Model):
    address = models.TextField(unique=True)
    mapping = models.ForeignKey('imaging.WindowsMapping', related_name='macAddresses')

    def __str__(self):
        return self.address

# serializers.py
class WindowsCompleteMappingSerializer(serializers.Serializer):
    id = serializers.Field()
    macAddresses = serializers.RelatedField(many=True)
    clientId = serializers.Field()

Take a look through the documentation on serializer fields to get a better idea of the various ways you can represent relationships in your API.