sunwarr10r
sunwarr10r

Reputation: 4797

Django Rest Framework Serializers: read_only parameter has no effect

Read Only settings doesn't work properly.

No matter if I use read_only_fields:

read_only_fields = ('id', 'user', 'created_at', 'account_type', 'balance', 'iban')

or read_only for each serializer field:

class BankAccountSerializer(serializers.ModelSerializer):

    id = serializers.StringRelatedField(read_only=True) 
    user = serializers.StringRelatedField(read_only=True)
    created_at = serializers.SerializerMethodField(read_only=True)
    account_name = serializers.StringRelatedField(read_only=False)
    account_type = serializers.StringRelatedField(read_only=True)
    balance = serializers.StringRelatedField(read_only=True)
    iban = serializers.StringRelatedField(read_only=True)

    class Meta:
        model = BankAccount
        fields = '__all__'

    def get_created_at(self, instance):
        return instance.created_at.strftime("%B %d %Y")

This is what my database model looks like:

class BankAccount(models.Model):
    id = models.AutoField(primary_key=True)
    user = models.ForeignKey(settings.AUTH_USER_MODEL,
                             on_delete=models.CASCADE,
                             related_name='bankaccounts')
    created_at = models.DateTimeField(auto_now_add=True)
    account_name = models.CharField(max_length=255)
    account_type = models.CharField(max_length=10,
                                    choices=POSITIONS)
    balance = models.DecimalField(decimal_places=2, max_digits=12)
    iban = models.CharField(max_length=22)

    def __str__(self):
        return str(self.iban)

My permission classes are the following ones:

permission_classes = [IsUserOrReadOnly, IsAuthenticated]

Whereby the custom IsUserOrReadOnly class looks like this:

class IsUserOrReadOnly(permissions.BasePermission):
    def has_object_permission(self, request, view, obj):
        if request.method in permissions.SAFE_METHODS:
            return True
        return obj.user == request.user

My serializer/view looks like this:

class BankAccountViewSet(viewsets.ModelViewSet):
    queryset = BankAccount.objects.all()
    lookup_field = "iban"
    serializer_class = BankAccountSerializer
    permission_class = [IsUserOrReadOnly, IsAuthenticated]

What I get from the api endpoint (options method response) is not the result I am expecting, means the field account_name is still "read_only": true, as can be seen in the browsable api output:

"actions": {
    "PUT": {
        "id": {
            "type": "field",
            "required": false,
            "read_only": true,
            "label": "Id"
        },
        "user": {
            "type": "field",
            "required": false,
            "read_only": true,
            "label": "User"
        },
        "created_at": {
            "type": "field",
            "required": false,
            "read_only": true,
            "label": "Created at"
        },
        "account_name": {
            "type": "field",
            "required": false,
            "read_only": true,
            "label": "Account name"
        },
        "account_type": {
            "type": "field",
            "required": false,
            "read_only": true,
            "label": "Account type"
        },
        "balance": {
            "type": "field",
            "required": false,
            "read_only": true,
            "label": "Balance"
        },
        "iban": {
            "type": "field",
            "required": false,
            "read_only": true,
            "label": "Iban"
        }
    }
}

What could here be wrong?

Upvotes: 0

Views: 1780

Answers (2)

JPG
JPG

Reputation: 88689

You don't have to specify all the model fields in your serializer since you are using the ModelSerializer
Try the below serializer,

class BankAccountSerializer(serializers.ModelSerializer):
    created_at = serializers.SerializerMethodField()

    class Meta:
        model = BankAccount
        fields = '__all__'
        read_only_fields = ('id', 'user', 'created_at', 'account_type', 'balance', 'iban')

    def get_created_at(self, instance):
        return instance.created_at.strftime("%B %d %Y")

Upvotes: 3

Oleg Russkin
Oleg Russkin

Reputation: 4412

StringRelatedField is read_only.

You can try using SlugRelatedField instead or nested serializers.

Upvotes: 2

Related Questions