-
Notifications
You must be signed in to change notification settings - Fork 106
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
shopinvader_api_address: support partial update #1443
shopinvader_api_address: support partial update #1443
Conversation
IIRC the full update was a conscious decision in #1361, but I can't find the discussion right now. How do you reset values to null with this approach? What is the benefit of a partial update? If we do this, this must be explained clearly in the service docs visible in swagger. |
@sbidoul The reset of value is easy. If a key for a field is into the json rqst, that means that a new value is provided and must be set as new value (even if null)... In the schema definition, we can specify that a field is required and nullable. Required means that the field must be present into the json. Nullable applies to the provided value.....
The call to the method # nullable and optional
class A(BaseModel):
name: str | None = None
print(A().model_dump(exclude_unset=True))
>> {}
print(A(name=None).model_dump(exclude_unset=True))
>> {'name': None}
print(A(name="a").model_dump(exclude_unset=True))
>> {'name': 'a'}
# nullable and required
class A(BaseModel):
name: str | None
print(A().model_dump(exclude_unset=True))
>> pydantic_core._pydantic_core.ValidationError: 1 validation error for A
>> name
>> Field required [type=missing, input_value={}, input_type=dict]
>> For further information visit https://errors.pydantic.dev/2.3/v/missing
print(A(name=None).model_dump(exclude_unset=True))
>> {'name': None}
print(A(name="a").model_dump(exclude_unset=True))
>> {'name': 'a'}
# not nullable and required
class A(BaseModel):
name: str
print(A().model_dump(exclude_unset=True))
>> pydantic_core._pydantic_core.ValidationError: 1 validation error for A
>> name
>> Field required [type=missing, input_value={}, input_type=dict]
>> For further information visit https://errors.pydantic.dev/2.3/v/missing
print(A(name=None).model_dump(exclude_unset=True))
>> pydantic_core._pydantic_core.ValidationError: 1 validation error for A
>> name
>> Input should be a valid string [type=string_type, input_value=None, input_type=NoneType]
>> For further information visit https://errors.pydantic.dev/2.3/v/string_type
print(A(name="a").model_dump(exclude_unset=True))
>> {'name': 'a'}
# not nullable and optional
class A(BaseModel):
name: str = None
print(A().model_dump(exclude_unset=True))
>> {}
print(A(name=None).model_dump(exclude_unset=True))
>> pydantic_core._pydantic_core.ValidationError: 1 validation error for A
>> name
>> Input should be a valid string [type=string_type, input_value=None, input_type=NoneType]
>> For further information visit https://errors.pydantic.dev/2.3/v/string_type
print(A(name="a").model_dump(exclude_unset=True))
>> {'name': 'a'} That's being said, IMO the definition of the class AddressUpdate(StrictExtendableBaseModel):
"""
used to update address (res.partner)
state and country can be name or code
"""
name: str | None = None
street: str | None = None
street2: str | None = None
zip: str | None = None
city: str | None = None
phone: str | None = None
email: str | None = None
state_id: int | None = None
country_id: int | None = None At least the name can't be null. class AddressUpdate(StrictExtendableBaseModel):
"""
used to update address (res.partner)
state and country can be name or code
"""
name: str = None
... |
Ok, thanks for the detailed explanation. This still raises some questions in my head:
|
Forcing the front to pass the email when updating the invoicing address may be a problem. |
It's true at first and could lead to error for beginners.
model_dump doesn't dump to JSON. It dumps python value. ( class B(BaseModel):
f: float
dt: datetime
d: date
print(B(f="5.1", d="2023-11-03", dt="2023-11-03T11:12:12Z").model_dump(exclude_unset=True))
>> {'f': 5.1, 'dt': datetime.datetime(2023, 11, 3, 11, 12, 12, tzinfo=TzInfo(UTC)), 'd': datetime.date(2023, 11, 3)}
??
Why? We still need to define the conversion from the schema to the value to write.
For sure |
Ah, TIL, thanks! Ok then. So do we want to set this as a pattern that all shopinvader update services should work this way? |
Pros:
Less:
The last point can be addressed by adopting a different pattern for processing the data received. def to_res_partner_vals(self) -> dict:
values = self.model_dump(exclude_unset=True)
vals = {}
if "name" in values:
vals["name"] = self.name
if "street" in values:
vals["street"] = self.street
...
return vals Personally, I am in favour of adopting as a guideline the development of update services that allow partial updating. ping @AnizR @qgroulard @simahawk @hparfr @marielejeune @GuillemCForgeFlow @cristiano-one |
I think that partial update can be really useful and I am not against it! Shouldn't we make a distinction between partial update and total update? This could be done using the |
/ocabot merge patch |
On my way to merge this fine PR! |
@lmignon your merge command was aborted due to failed check(s), which you can inspect on this commit of 16.0-ocabot-merge-pr-1443-by-lmignon-bump-patch. After fixing the problem, you can re-issue a merge command. Please refrain from merging manually as it will most probably make the target branch red. |
I agree. Had to fight we this in past implementations.
Good point. |
I'm investigating the test failure. Probably related to OCA/rest-framework#402 |
Tests fixed in #1486 |
/ocabot merge patch |
Hey, thanks for contributing! Proceeding to merge this for you. |
It looks like something changed on |
Congratulations, your PR was merged at d568329. Thanks a lot for contributing to shopinvader. ❤️ |
Add partial update on address (see here : https://github.com/shopinvader/odoo-shopinvader/pull/1425/files#r1361596661)
@sbidoul @lmignon