Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exception caught for serializer field ReadOnlyField with a FileField source #1303

Closed
jennydaman opened this issue Oct 1, 2024 · 3 comments
Closed

Comments

@jennydaman
Copy link

Describe the bug

I have a simple file server:

from django.db import models
from django.shortcuts import render
from rest_framework import generics, serializers

class FooFile(models.Model):
    fname = models.FileField(max_length=1024, unique=True)

class FooFileSerializer(serializers.HyperlinkedModelSerializer):
    fname = serializers.FileField(use_url=False, required=False)
    fsize = serializers.ReadOnlyField(source="fname.size")  # <-- herein lies the problem

    class Meta:
        model = FooFile
        fields = ("fname", "fsize")

class FooFileList(generics.ListCreateAPIView):
    http_method_names = ["get", "post"]
    serializer_class = FooFileSerializer
    queryset = FooFile.objects.all()

Generating the OpenAPI schema using drf-spectacular prints this warning:

 Warning [FooFileList > FooFileSerializer]: could not resolve field on model <class 'foo.models.FooFile'> with path "fname.size". This is likely a custom field that does some unknown magic. Maybe consider annotating the field/property? Defaulting to "string". (Exception: 'NoneType' object has no attribute '_meta')

To Reproduce

See https:/jennydaman/spectacular-fsize-bug

Expected behavior

No warnings. components.schemas.FooFile.properties.fsize.type should be number.

@jennydaman
Copy link
Author

I would like some kind of workaround, but I am not sure how one would use extend_schema_field here. For the time being, I am going to ignore the warning and use a post-processing hook to fix FooFile.properties.fsize.type.

@tfranzel
Copy link
Owner

tfranzel commented Oct 1, 2024

Hi, you can do this serializers.ReadOnlyField(source="fname") but not "fname.size". We can navigate arbitrary chains of models/fields/properties, but not attributes of field.

We have a lot of navigation power in there, but even if we would have accessed size, we still could not know what type it is. It is dynamically added and a implementation detail of that specific model field.

There are a few ways to get there (e.g. typed property function on the model) with ReadOnlyField, but I would use a SerializerMethodField on the seriailzer. It is slightly more vebose, but it is clean and typed:

fsize = serializers.SerializerMethodField()

def get_fsize(self, object) -> int:
    return object.fname.size

@jennydaman
Copy link
Author

@tfranzel Thank you for the solution!

@tfranzel tfranzel closed this as completed Oct 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants