@@ -21,6 +21,15 @@ An easy way to add or create partials for Pydantic models.
2121
2222[ // ] : # ( --8<-- [start:readme] )
2323
24+ ## Important Upgrade from v1.x Notes
25+
26+ I decided to make the default behavior of ` PartialModel ` not be automatic anymore.
27+
28+ I made a new class named ` AutoPartialModel ` that works exactly the same as the old v1.x ` PartialModel ` previously did.
29+
30+ To upgrade, simply replace ` PartialModel ` with ` AutoPartialModel ` , and things will work exactly as they did before.
31+ The ` auto_partials ` configuration option is still present and if present will still override the base-class setting.
32+
2433## Quick Start
2534
2635### Install
@@ -41,21 +50,78 @@ You can create from scratch, or convert existing models to be Partials.
4150The main purpose will be to add to exiting models, and hence the default
4251behavior of making all non-default fields partials (configurable).
4352
53+ ### Two Partial Base Class Options
54+
55+ There are two options to inherit from:
56+
57+ - ` PartialModel `
58+ - With this one, you must explicitly set which fields are partial
59+ - To get correct static type checking, you also can also set a partial field's default value to ` Missing ` .
60+ - ` AutoPartialModel `
61+ - This automatically applies partial behavior to every attribute that does not already have a default value.
62+
63+
4464Let's first look at a basic example.
4565
46- ### Basic Example
66+ ### Explicitly Defined Partials - Basic Example
4767
48- Very basic example of a simple model follows:
68+ Very basic example of a simple model with explicitly defined partial fields, follows:
4969
5070``` python
51- from pydantic_partials import PartialModel, Missing
52-
71+ from pydantic_partials import PartialModel, Missing, Partial, MissingType
72+ from pydantic import ValidationError
5373
5474class MyModel (PartialModel ):
75+ some_field: str
76+ partial_field: Partial[str ] = Missing
77+
78+ # Alternate Syntax:
79+ alternate_syntax_partial_field: str | MissingType = Missing
80+
81+
82+ # By default, `Partial` fields without any value will get set to a
83+ # special `Missing` type. Any field that is set to Missing is
84+ # excluded from the model_dump/model_dump_json.
85+ obj = MyModel(some_field = ' a-value' )
86+ assert obj.partial_field is Missing
87+ assert obj.model_dump() == {' some_field' : ' a-value' }
88+
89+ # You can set the real value at any time, and it will behave like expected.
90+ obj.partial_field = ' hello'
91+ assert obj.partial_field == ' hello'
92+ assert obj.model_dump() == {' some_field' : ' a-value' , ' partial_field' : ' hello' }
93+
94+ # You can always manually set a field to `Missing` directly.
95+ obj.partial_field = Missing
96+
97+ # And now it's removed from the model-dump.
98+ assert obj.model_dump() == {' some_field' : ' a-value' }
99+
100+ # The json dump is also affected in the same way.
101+ assert obj.model_dump_json() == ' {"some_field":"a-value"}'
102+
103+ try :
104+ # This should produce an error because
105+ # `some_field` is a required field.
106+ MyModel()
107+ except ValidationError as e:
108+ print (f ' Pydantic will state `some_field` + `value` are required: { e} ' )
109+ else :
110+ raise Exception (' Pydantic should have required `some_field`.' )
111+ ```
112+
113+ ### Automatically Defined Partials - Basic Example
114+
115+ Very basic example of a simple model with automatically defined partial fields, follows:
116+
117+ ``` python
118+ from pydantic_partials import AutoPartialModel, Missing
119+
120+ class MyModel (AutoPartialModel ):
55121 some_attr: str
56122 another_field: str
57123
58- # By default, Partial fields without any value will get set to a
124+ # By default, automatic defined partial fields without any value will get set to a
59125# special `Missing` type. Any field that is set to Missing is
60126# excluded from the model_dump/model_dump_json.
61127obj = MyModel()
@@ -91,10 +157,10 @@ This includes any inherited Pydantic fields (from a superclass).
91157
92158### Inheritable
93159
94- You can inherit from a model to make a partial-version of the inherited fields:
160+ With ` AutoPartialModel ` , you can inherit from a model to make an automatic partial-version of the inherited fields:
95161
96162``` python
97- from pydantic_partials import PartialModel , Missing
163+ from pydantic_partials import AutoPartialModel , Missing
98164from pydantic import ValidationError, BaseModel
99165
100166class TestModel (BaseModel ):
@@ -109,11 +175,11 @@ try:
109175except ValidationError as e:
110176 print (f ' Pydantic will state `name` + `value` are required: { e} ' )
111177else :
112- raise Exception (' Field `required_decimal` should be required.' )
178+ raise Exception (' Pydantic should have required `required_decimal` .' )
113179
114180 # We inherit from `TestModel` and add `PartialModel` to the mix.
115181
116- class PartialTestModel (PartialModel , TestModel ):
182+ class PartialTestModel (AutoPartialModel , TestModel ):
117183 pass
118184
119185# `PartialTestModel` can now be allocated without the required fields.
@@ -137,7 +203,7 @@ Notice that if a field has a default value, it's used instead of marking it as `
137203Also, the ` Missing ` sentinel value is a separate value vs ` None ` , allowing one to easily
138204know if a value is truly just missing or is ` None ` /` Null ` .
139205
140- ### Exclude Fields From Auto Partials
206+ ### Exclude Fields from Automatic Partials (AutoPartialModel)
141207
142208You can exclude specific fields from the automatic partials via these means:
143209
@@ -155,12 +221,12 @@ You can override an excluded value by explicitly marking a field as Partial via
155221Here is an example using the ` AutoPartialExclude ` method, also showing how it can inherit.
156222
157223``` python
158- from pydantic_partials import PartialModel , AutoPartialExclude, Missing
224+ from pydantic_partials import AutoPartialModel , AutoPartialExclude, Missing
159225from pydantic import BaseModel, ValidationError
160226from datetime import datetime
161227import pytest
162228
163- class PartialRequired (PartialModel ):
229+ class PartialRequired (AutoPartialModel ):
164230 id : AutoPartialExclude[str ]
165231 created_at: AutoPartialExclude[datetime]
166232
@@ -181,10 +247,11 @@ with pytest.raises(
181247 r ' id[\w\W ]* Field required[\w\W ]* '
182248 r ' created_at[\w\W ]* Field required'
183249):
184- PartialTestModel()
250+ # This should raise a 'ValidationError'
251+ PartialTestModel() # type: ignore
185252
186253# If we give them values, we get no ValidationError
187- obj = PartialTestModel(id = ' some-value' , created_at = datetime.now())
254+ obj = PartialTestModel(id = ' some-value' , created_at = datetime.now()) # type: ignore
188255
189256# And fields have the expected values.
190257assert obj.id == ' some-value'
@@ -193,16 +260,21 @@ assert obj.name is Missing
193260
194261### Auto Partials Configuration
195262
196- You can turn off automatically applying partials to all non-defaulted fields
197- via ` auto_partials ` class argument or modeL_config option:
263+ Normally you would simply inherit from either ` PartialModel ` or ` AutoPartialModel ` , depending on the desired behavior you want.
264+
265+ But you can also configure the auto-partials aspect via class paramters or the ` model_config ` attribute:
198266
199267``` python
200- from pydantic_partials import PartialModel, PartialConfigDict
268+ from pydantic_partials import PartialModel, PartialConfigDict, AutoPartialModel
201269
202- class TestModel1 (PartialModel , auto_partials = False ):
270+ # `PartialModel` uses `auto_partials` as `False` by default, but we can override that if you want via class argument:
271+ class TestModel1 (PartialModel , auto_partials = True ):
203272 ...
204273
205- class TestModel2 (PartialModel ):
274+ # Or via `model_config`
275+ # (PartialConfigDict inherits from Pydantic's `ConfigDict`,
276+ # so you have all of Pydantic's options still available).
277+ class TestModel2 (AutoPartialModel ):
206278 model_config = PartialConfigDict(auto_partials = False )
207279 ...
208280```
@@ -211,29 +283,27 @@ You can disable this automatic function. This means you have complete control of
211283can be partial or not. You can use either the generic ` Partial[...] ` generic or a union with ` MissingType `
212284to mark a field as a partial field. The generic simple makes the union to MissingType for you.
213285
214- Example of disabling auto_partials:
215-
216286``` python
217287from pydantic_partials import PartialModel, Missing, MissingType, Partial
218288from decimal import Decimal
219289from pydantic import ValidationError
220290
221- class TestModel (PartialModel , auto_partials = False ):
291+ class TestModel (PartialModel ):
222292 # Can use `Partial` generic type
223293 partial_int: Partial[int ] = Missing
224-
294+
225295 # Or union with `MissingType`
226296 partial_str: str | MissingType
227-
297+
228298 required_decimal: Decimal
229-
299+
230300try :
231301 TestModel()
232302except ValidationError as e:
233303 print (f ' Pydantic will state `required_decimal` is required: { e} ' )
234304else :
235305 raise Exception (' Pydantic should have required `required_decimal`.' )
236-
306+
237307obj = TestModel(required_decimal = ' 1.34' )
238308
239309# You can find out at any time if a field is missing or not:
0 commit comments