Skip to content

fix backlink serialization when link data is fetched#1171

Closed
alexdlukens wants to merge 3 commits intoBeanieODM:mainfrom
alexdlukens:backlink-fastapi-validation-error
Closed

fix backlink serialization when link data is fetched#1171
alexdlukens wants to merge 3 commits intoBeanieODM:mainfrom
alexdlukens:backlink-fastapi-validation-error

Conversation

@alexdlukens
Copy link

Hello,

I have noticed in FastAPI, if using a model with a BackLink as the return type for a route, the data will fail validation due to an error in validating the BackLink field.

On further inspection, it seems that there was no serialization handling for the BackLink when the field contains valid data from the linked model.

I took the similar approach from the Link field, using the serialize method. Find the Link implementation here

Added a test that checks for this to the FastAPI tests.

Best,
Alex

@alexdlukens alexdlukens force-pushed the backlink-fastapi-validation-error branch 2 times, most recently from 69cc101 to 2454f8e Compare May 9, 2025 03:50
@alexdlukens alexdlukens force-pushed the backlink-fastapi-validation-error branch from 2454f8e to 751180e Compare May 9, 2025 04:20
Copy link
Member

@staticxterm staticxterm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi,
thank you for the PR.
Roman previously noted that BackLinks should only be "virtual" fields, and not be serializable.

Please provide a meaningful use case scenario where one could benefit from the serialization of a BackLink. Later, if there really is a valid use case for this, it should be added to the Beanie docs as well.

),
serialization=core_schema.plain_serializer_function_ser_schema(
lambda instance: cls.to_dict(instance),
lambda instance: cls.serialize(instance),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've placed a comment here on line 569 noting to other developers that BackLinks should not be serialized (as I've understood from Roman's response in some GH issue comment).

return house


@house_router.get("/person/{id}", response_model=Person)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This (response_model=Person) is not supported as BackLinks should not be serializable.
BackLink fetching (e.g. Person.get(fetch_links=True)) was never implemented nor specified as supported in the docs:
https://beanie-odm.dev/tutorial/relations/#back-links

It is not possible to fetch() this virtual link after the initial search.

One problem being that it would lead to infinite recursion if "nesting_depth" was not specified, as you've done below.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am okay with not having the backlink field in the serialized result, but I believe serialization should not raise an error if the backlink field was fetched. Please see the minimal test I have added showing this behavior in cf4a18d

resp2 = await api_client.get(f"/v1/person/{person_id}")

resp2_json = resp2.json()
assert resp2_json["name"] == payload["owner"]["name"][-3:]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assertion is kind of meaningless here. What is the point in fetching the related House "owner" from the document in a Person collection?
I struggle to see the real benefit of this functionality from this test alone.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This example is a bit weak, but I think making CRUD routes for a database model is a common use case. As in routes that would insert, get, update, delete a database model. This flow is broken if we cannot serialize a model when a backlink has been fetched

I guess to make this work one would have to not include backlinks in the model at all, or never fetch_links...

def serialize(value: Union[BackLink[T], BaseModel]):
if isinstance(value, BackLink):
return value.to_dict()
return value.model_dump(mode="json")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the use case for this "else if not a BackLink" scenario? Please provide a test case showcasing this, similar to the test_id_types_serialized_when_dumping_to_json test case which does it for the Link type.

@alexdlukens
Copy link
Author

Hello, please find the minimal test I have added to exhibit the problem I was seeing. This test raises an error when executed on the main branch:

FAILED tests/odm/test_relations.py::TestOther::test_dump_model_with_fetched_backlink - pydantic_core._pydantic_core.PydanticSerializationError: Error serializing to JSON: PydanticSerializationError: Error calling function `<lambda>`: AttributeError: 'DocumentWithLink' object has no attribute 'document_class'

@alexdlukens
Copy link
Author

Hello, I see the issue with the recursion as you mention. I still think the behavior I am seeing is a bug, but I understand why dumping the fetched model will not work as I have written.

GIven this, I am not sure what the expected result should be. If we want to return {"collection": "<class_name>"} in this situation, I propose something like in https://github.com/BeanieODM/beanie/compare/main...alexdlukens:beanie:fix-backlink-return-collection?expand=1

Best,

@github-actions
Copy link
Contributor

github-actions bot commented Jul 4, 2025

This PR is stale because it has been open 45 days with no activity.

@alexdlukens
Copy link
Author

Superceded by PR #1207

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

Successfully merging this pull request may close these issues.

2 participants