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

Accessing serializer.fields inside a nested serializer breaks the use of only/exclude #52

Open
philipstarkey opened this issue Mar 8, 2022 · 5 comments

Comments

@philipstarkey
Copy link
Contributor

For some reason that I can't quite figure out, it seems that accessing self.fields inside a nested and expanded serializer causes the only/exclude URL query args to be ignored for that nested serializer (as in I get all fields in the response for any nested serializer that accesses self.fields inside __init__).

This seems odd to me, and I assume is caused by some side-effect of the field binding that rest_framework does when self.fields is accessed. I've, so far, managed to implement work arounds that either use extra_kwargs or self.get_fields() but many examples/stack-overflow answers suggest using self.fields (e.g. to override the widget used for a field in the automatically generated create/update form in the browsable API).

Accessing self.fields in __init__ of the top level serializer doesn't break only/exclude

@evenicoulddoit
Copy link
Owner

Interesting! I quickly just looked at DRF and confirmed my suspicions here based on your comments:

    @cached_property
    def fields(self):
        """
        A dictionary of {field_name: field_instance}.
        """
        # `fields` is evaluated lazily. We do this to ensure that we don't
        # have issues importing modules that use ModelSerializers as fields,
        # even if Django's app-loading stage has not yet run.
        fields = BindingDict(self)
        for key, value in self.get_fields().items():
            fields[key] = value
        return fields

So, looking at this - you can most likely use self.get_fields() depending on your use case, and it won't have side effects

@philipstarkey
Copy link
Contributor Author

Following up on this, you can use get_fields() for anything that doesn't involve mutation of the field state. This is because get_fields() returns new fields each time (and that's why fields is a cached property). So it means you can't, for example, use get_fields() to change the queryset of a related field.

The reason why accessing self.fields in __init__ triggers the bug is related to #48. self.context is not available to nested serializers until after the parent serializer calls field.bind(), because the DRF context property uses self.parent (via self.root) to access the context of the top level serializer, and self.parent isn't set until the nested serializer is bound to the parent.

So while this is really annoying (for me), I'm not sure it's fixable without breaking DRF conventions about when context becomes available?

@evenicoulddoit
Copy link
Owner

Just a question: why do you want to access the fields from within the constructor anyway? Perhaps that might lead us to some conclusion

@philipstarkey
Copy link
Contributor Author

I'm applying a permission based filter to (related) field querysets so that the available choices for related objects during a POST/PUT/PATCH request are dynamically determined based on the permissions of the current user. These fields are also potentially expandable in GET requests (hence the use of your library!)

@evenicoulddoit
Copy link
Owner

Ah I see. And so you're trying to access the nested fields to see if your request user should also have access to those?

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