-
Notifications
You must be signed in to change notification settings - Fork 276
fix backlink serialization when link data is fetched #1171
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -516,6 +516,11 @@ def __modify_schema__(cls, field_schema: Dict[str, Any]): | |
| } | ||
| ) | ||
|
|
||
| def serialize(self): | ||
| if isinstance(self, Link): | ||
| return self.to_dict() | ||
| return self.dict() | ||
|
|
||
| def to_ref(self): | ||
| return self.ref | ||
|
|
||
|
|
@@ -524,7 +529,7 @@ def to_dict(self): | |
|
|
||
|
|
||
| if not IS_PYDANTIC_V2: | ||
| ENCODERS_BY_TYPE[Link] = lambda o: o.to_dict() | ||
| ENCODERS_BY_TYPE[Link] = lambda o: o.serialize() | ||
|
|
||
|
|
||
| class BackLink(Generic[T]): | ||
|
|
@@ -535,6 +540,12 @@ def __init__(self, document_class: Type[T]): | |
|
|
||
| if IS_PYDANTIC_V2: | ||
|
|
||
| @staticmethod | ||
| def serialize(value: Union[BackLink[T], BaseModel]): | ||
| if isinstance(value, BackLink): | ||
| return value.to_dict() | ||
| return value.model_dump(mode="json") | ||
|
|
||
| @classmethod | ||
| def wrapped_validate( | ||
| cls, source_type: Type[Any], handler: GetCoreSchemaHandler | ||
|
|
@@ -565,7 +576,7 @@ def __get_pydantic_core_schema__( | |
| values_schema=core_schema.any_schema(), | ||
| ), | ||
| serialization=core_schema.plain_serializer_function_ser_schema( | ||
| lambda instance: cls.to_dict(instance), | ||
| lambda instance: cls.serialize(instance), | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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_schema=core_schema.dict_schema(), | ||
| when_used="json-unless-none", | ||
| ), | ||
|
|
@@ -610,13 +621,18 @@ def __modify_schema__(cls, field_schema: Dict[str, Any]): | |
| } | ||
| ) | ||
|
|
||
| def serialize(self): | ||
| if isinstance(self, BackLink): | ||
| return self.to_dict() | ||
| return self.dict() | ||
|
|
||
| def to_dict(self) -> dict[str, str]: | ||
| document_class = DocsRegistry.evaluate_fr(self.document_class) # type: ignore | ||
| return {"collection": document_class.get_collection_name()} | ||
|
|
||
|
|
||
| if not IS_PYDANTIC_V2: | ||
| ENCODERS_BY_TYPE[BackLink] = lambda o: o.to_dict() | ||
| ENCODERS_BY_TYPE[BackLink] = lambda o: o.serialize() | ||
|
|
||
|
|
||
| class IndexModelField: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -71,3 +71,8 @@ async def create_house_new(house: House = Body(...)): | |
| await house.save(link_rule=WriteRules.WRITE) | ||
| await house.sync() | ||
| return house | ||
|
|
||
|
|
||
| @house_router.get("/person/{id}", response_model=Person) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
One problem being that it would lead to infinite recursion if "nesting_depth" was not specified, as you've done below.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
| async def get_person(id: PydanticObjectId): | ||
| return await Person.get(id, fetch_links=True, nesting_depth=1) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -66,3 +66,22 @@ async def test_create_house_new(api_client): | |
| assert resp_json["name"] == payload["name"] | ||
| assert resp_json["owner"]["name"] == payload["owner"]["name"][-3:] | ||
| assert resp_json["owner"]["house"]["collection"] == "House" | ||
|
|
||
|
|
||
| async def test_get_person(api_client): | ||
| payload = { | ||
| "name": "FreshHouse", | ||
| "owner": {"name": "will_be_overridden_to_Bob"}, | ||
| } | ||
| resp = await api_client.post("/v1/house", json=payload) | ||
| resp_json = resp.json() | ||
|
|
||
| person_id = resp_json["owner"].get("id") | ||
| if person_id is None: | ||
| person_id = resp_json["owner"].get("_id") | ||
| assert person_id is not None | ||
|
|
||
| resp2 = await api_client.get(f"/v1/person/{person_id}") | ||
|
|
||
| resp2_json = resp2.json() | ||
| assert resp2_json["name"] == payload["owner"]["name"][-3:] | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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... |
||
There was a problem hiding this comment.
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_jsontest case which does it for the Link type.