- [Pydantic Partials](#pydantic-partials)
* [Documentation](#documentation)
* [Important Upgrade from v1.x to 2.x Notes](#important-upgrade-from-v1x-to-2x-notes)
* [Quick Start](#quick-start)
+ [Install](#install)
+ [Introduction](#introduction)
+ [Two Partial Base Class Options](#two-partial-base-class-options)
+ [Explicitly Defined Partials - Basic Example](#explicitly-defined-partials---basic-example)
+ [Automatically Defined Partials - Basic Example](#automatically-defined-partials---basic-example)
* [More Details](#more-details)
+ [Inheritable](#inheritable)
+ [Exclude Fields from Automatic Partials (AutoPartialModel)](#exclude-fields-from-automatic-partials-autopartialmodel)
+ [Auto Partials Configuration](#auto-partials-configuration)
# Pydantic Partials
An easy way to add or create partials for Pydantic models.
[](https://pypi.org/project/pydantic-partials/)
## Documentation
**[📄 Detailed Documentation](https://joshorr.github.io/pydantic-partials/latest/)** | **[🐍 PyPi](https://pypi.org/project/pydantic-partials/)**
[//]: # (--8<-- [start:readme])
## Important Upgrade from v1.x to 2.x Notes
I decided to make the default behavior of `PartialModel` not be automatic anymore.
I made a new class named `AutoPartialModel` that works exactly the same as the old v1.x `PartialModel` previously did.
To upgrade, simply replace `PartialModel` with `AutoPartialModel`, and things will work exactly as they did before.
The `auto_partials` configuration option is still present and if present will still override the base-class setting.
## Quick Start
### Install
```shell
poetry install pydantic-partials
```
or
```shell
pip install pydantic-partials
```
### Introduction
You can create from scratch, or convert existing models to be Partials.
The main purpose will be to add to exiting models, and hence the default
behavior of making all non-default fields partials (configurable).
### Two Partial Base Class Options
There are two options to inherit from:
- `PartialModel`
- With this one, you must explicitly set which fields are partial
- To get correct static type checking, you also can also set a partial field's default value to `Missing`.
- `AutoPartialModel`
- This automatically applies partial behavior to every attribute that does not already have a default value.
Let's first look at a basic example.
### Explicitly Defined Partials - Basic Example
Very basic example of a simple model with explicitly defined partial fields, follows:
```python
from pydantic_partials import PartialModel, Missing, Partial, MissingType
from pydantic import ValidationError
class MyModel(PartialModel):
some_field: str
partial_field: Partial[str] = Missing
# Alternate Syntax:
alternate_syntax_partial_field: str | MissingType = Missing
# By default, `Partial` fields without any value will get set to a
# special `Missing` type. Any field that is set to Missing is
# excluded from the model_dump/model_dump_json.
obj = MyModel(some_field='a-value')
assert obj.partial_field is Missing
assert obj.model_dump() == {'some_field': 'a-value'}
# You can set the real value at any time, and it will behave like expected.
obj.partial_field = 'hello'
assert obj.partial_field == 'hello'
assert obj.model_dump() == {'some_field': 'a-value', 'partial_field': 'hello'}
# You can always manually set a field to `Missing` directly.
obj.partial_field = Missing
# And now it's removed from the model-dump.
assert obj.model_dump() == {'some_field': 'a-value'}
# The json dump is also affected in the same way.
assert obj.model_dump_json() == '{"some_field":"a-value"}'
try:
# This should produce an error because
# `some_field` is a required field.
MyModel()
except ValidationError as e:
print(f'Pydantic will state `some_field` is required: {e}')
else:
raise Exception('Pydantic should have required `some_field`.')
```
### Automatically Defined Partials - Basic Example
Very basic example of a simple model with automatically defined partial fields, follows:
```python
from pydantic_partials import AutoPartialModel, Missing
class MyModel(AutoPartialModel):
some_attr: str
another_field: str
# By default, automatic defined partial fields without any value will get set to a
# special `Missing` type. Any field that is set to Missing is
# excluded from the model_dump/model_dump_json.
obj = MyModel()
assert obj.some_attr is Missing
assert obj.model_dump() == {}
# You can set the real value at any time, and it will behave like expected.
obj.some_attr = 'hello'
assert obj.some_attr is 'hello'
assert obj.model_dump() == {'some_attr': 'hello'}
# You can always manually set a field to `Missing` directly.
obj.some_attr = Missing
# And now it's removed from the model-dump.
assert obj.model_dump() == {}
# The json dump is also affected in the same way.
assert obj.model_dump_json() == '{}'
# Any non-missing fields will be included when dumping/serializing model.
obj.another_field = 'assigned-value'
# After dumping again, we have `another_field` outputted.
# The `some_attr` field is not present since it's still `Missing`.
assert obj.model_dump() == {'another_field': 'assigned-value'}
```
By default, all fields without a default value will have the ability to be partial,
and can be missing from both validation and serialization.
This includes any inherited Pydantic fields (from a superclass).
## More Details
### Inheritable
With `AutoPartialModel`, you can inherit from a model to make an automatic partial-version of the inherited fields:
```python
from pydantic_partials import AutoPartialModel, Missing
from pydantic import ValidationError, BaseModel
class TestModel(BaseModel):
name: str
value: str
some_null_by_default_field: str | None = None
try:
# This should produce an error because
# `name` and `value`are required fields.
TestModel()
except ValidationError as e:
print(f'Pydantic will state `name` + `value` are required: {e}')
else:
raise Exception('Pydantic should have required `required_decimal`.')
# We inherit from `TestModel` and add `PartialModel` to the mix.
class PartialTestModel(AutoPartialModel, TestModel):
pass
# `PartialTestModel` can now be allocated without the required fields.
# Any missing required fields will be marked with the `Missing` value
# and won't be serialized out.
obj = PartialTestModel(name='a-name')
assert obj.name == 'a-name'
assert obj.value is Missing
assert obj.some_null_by_default_field is None
# The `None` field value is still serialized out,
# only fields with a `Missing` value assigned are skipped.
assert obj.model_dump() == {
'name': 'a-name', 'some_null_by_default_field': None
}
```
Notice that if a field has a default value, it's used instead of marking it as `Missing`.
Also, the `Missing` sentinel value is a separate value vs `None`, allowing one to easily
know if a value is truly just missing or is `None`/`Null`.
### Exclude Fields from Automatic Partials (AutoPartialModel)
You can exclude specific fields from the automatic partials via these means:
- `AutoPartialExclude[...]`
- This puts a special `Annotated` item on field to mark it as excluded.
- `class PartialRequired(PartialModel, auto_partials_exclude={'id', 'created_at'}):`
- This way provides them via class argument `auto_partials_exclude`
- Or via the standard `model_config`
- `model_config = {'auto_partials_exclude': {'id', 'created_at'}}`
- A dict, using `auto_partials_exclude` as the key and a set of field names as the value.
Any of these methods are inheritable.
You can override an excluded value by explicitly marking a field as Partial via `some_field: Partial[str]`
Here is an example using the `AutoPartialExclude` method, also showing how it can inherit.
```python
from pydantic_partials import AutoPartialModel, AutoPartialExclude, Missing
from pydantic import BaseModel, ValidationError
from datetime import datetime
import pytest
class PartialRequired(AutoPartialModel):
id: AutoPartialExclude[str]
created_at: AutoPartialExclude[datetime]
class TestModel(BaseModel):
id: str
created_at: datetime
name: str
value: str
some_null_by_default_field: str | None = None
class PartialTestModel(TestModel, PartialRequired):
pass
# Will raise validation error for the two fields excluded from auto-partials
with pytest.raises(
ValidationError,
match=r'2 validation errors[\w\W]*'
r'id[\w\W]*Field required[\w\W]*'
r'created_at[\w\W]*Field required'
):
# This should raise a 'ValidationError'
PartialTestModel() # type: ignore
# If we give them values, we get no ValidationError
obj = PartialTestModel(id='some-value', created_at=datetime.now()) # type: ignore
# And fields have the expected values.
assert obj.id == 'some-value'
assert obj.name is Missing
```
### Auto Partials Configuration
Normally you would simply inherit from either `PartialModel` or `AutoPartialModel`, depending on the desired behavior you want.
But you can also configure the auto-partials aspect via class paramters or the `model_config` attribute:
```python
from pydantic_partials import PartialModel, PartialConfigDict, AutoPartialModel
# `PartialModel` uses `auto_partials` as `False` by default, but we can override that if you want via class argument:
class TestModel1(PartialModel, auto_partials=True):
...
# Or via `model_config`
# (PartialConfigDict inherits from Pydantic's `ConfigDict`,
# so you have all of Pydantic's options still available).
class TestModel2(AutoPartialModel):
model_config = PartialConfigDict(auto_partials=False)
...
```
You can disable this automatic function. This means you have complete control of exactly which field
can be partial or not. You can use either the generic `Partial[...]` generic or a union with `MissingType`
to mark a field as a partial field. The generic simple makes the union to MissingType for you.
```python
from pydantic_partials import PartialModel, Missing, MissingType, Partial
from decimal import Decimal
from pydantic import ValidationError
class TestModel(PartialModel):
# Can use `Partial` generic type
partial_int: Partial[int] = Missing
# Or union with `MissingType`
partial_str: str | MissingType
required_decimal: Decimal
try:
TestModel()
except ValidationError as e:
print(f'Pydantic will state `required_decimal` is required: {e}')
else:
raise Exception('Pydantic should have required `required_decimal`.')
obj = TestModel(required_decimal='1.34')
# You can find out at any time if a field is missing or not:
assert obj.partial_int is Missing
assert obj.partial_str is Missing
assert obj.required_decimal == Decimal('1.34')
```
[//]: # (--8<-- [end:readme])
Raw data
{
"_id": null,
"home_page": "https://github.com/joshorr/pydantic-partials",
"name": "pydantic-partials",
"maintainer": null,
"docs_url": null,
"requires_python": "<4.0,>=3.10",
"maintainer_email": null,
"keywords": null,
"author": "Josh Orr",
"author_email": "josh@orr.blue",
"download_url": "https://files.pythonhosted.org/packages/c8/ea/54c35bb1e7497881e60ba4ce01aa1a358c9aaf0b277c72523553ad95666a/pydantic_partials-2.0.1.tar.gz",
"platform": null,
"description": "- [Pydantic Partials](#pydantic-partials)\n * [Documentation](#documentation)\n * [Important Upgrade from v1.x to 2.x Notes](#important-upgrade-from-v1x-to-2x-notes)\n * [Quick Start](#quick-start)\n + [Install](#install)\n + [Introduction](#introduction)\n + [Two Partial Base Class Options](#two-partial-base-class-options)\n + [Explicitly Defined Partials - Basic Example](#explicitly-defined-partials---basic-example)\n + [Automatically Defined Partials - Basic Example](#automatically-defined-partials---basic-example)\n * [More Details](#more-details)\n + [Inheritable](#inheritable)\n + [Exclude Fields from Automatic Partials (AutoPartialModel)](#exclude-fields-from-automatic-partials-autopartialmodel)\n + [Auto Partials Configuration](#auto-partials-configuration)\n\n# Pydantic Partials\n\nAn easy way to add or create partials for Pydantic models.\n\n[](https://pypi.org/project/pydantic-partials/)\n\n## Documentation\n\n**[\ud83d\udcc4 Detailed Documentation](https://joshorr.github.io/pydantic-partials/latest/)** | **[\ud83d\udc0d PyPi](https://pypi.org/project/pydantic-partials/)**\n\n[//]: # (--8<-- [start:readme])\n\n## Important Upgrade from v1.x to 2.x Notes\n\nI decided to make the default behavior of `PartialModel` not be automatic anymore.\n\nI made a new class named `AutoPartialModel` that works exactly the same as the old v1.x `PartialModel` previously did.\n\nTo upgrade, simply replace `PartialModel` with `AutoPartialModel`, and things will work exactly as they did before.\nThe `auto_partials` configuration option is still present and if present will still override the base-class setting.\n\n## Quick Start\n\n### Install\n\n```shell\npoetry install pydantic-partials\n```\n\nor\n\n```shell\npip install pydantic-partials\n```\n\n### Introduction\n\nYou can create from scratch, or convert existing models to be Partials.\nThe main purpose will be to add to exiting models, and hence the default\nbehavior of making all non-default fields partials (configurable).\n\n### Two Partial Base Class Options\n\nThere are two options to inherit from:\n\n- `PartialModel`\n - With this one, you must explicitly set which fields are partial\n - To get correct static type checking, you also can also set a partial field's default value to `Missing`.\n- `AutoPartialModel`\n - This automatically applies partial behavior to every attribute that does not already have a default value.\n\n\nLet's first look at a basic example.\n\n### Explicitly Defined Partials - Basic Example\n\nVery basic example of a simple model with explicitly defined partial fields, follows:\n\n```python\nfrom pydantic_partials import PartialModel, Missing, Partial, MissingType\nfrom pydantic import ValidationError\n\nclass MyModel(PartialModel):\n some_field: str\n partial_field: Partial[str] = Missing\n \n # Alternate Syntax:\n alternate_syntax_partial_field: str | MissingType = Missing\n \n\n# By default, `Partial` fields without any value will get set to a\n# special `Missing` type. Any field that is set to Missing is\n# excluded from the model_dump/model_dump_json.\nobj = MyModel(some_field='a-value')\nassert obj.partial_field is Missing\nassert obj.model_dump() == {'some_field': 'a-value'}\n\n# You can set the real value at any time, and it will behave like expected.\nobj.partial_field = 'hello'\nassert obj.partial_field == 'hello'\nassert obj.model_dump() == {'some_field': 'a-value', 'partial_field': 'hello'}\n\n# You can always manually set a field to `Missing` directly.\nobj.partial_field = Missing\n\n# And now it's removed from the model-dump.\nassert obj.model_dump() == {'some_field': 'a-value'}\n\n# The json dump is also affected in the same way.\nassert obj.model_dump_json() == '{\"some_field\":\"a-value\"}'\n\ntry:\n # This should produce an error because\n # `some_field` is a required field.\n MyModel()\nexcept ValidationError as e:\n print(f'Pydantic will state `some_field` is required: {e}')\nelse:\n raise Exception('Pydantic should have required `some_field`.')\n```\n\n### Automatically Defined Partials - Basic Example\n\nVery basic example of a simple model with automatically defined partial fields, follows:\n\n```python\nfrom pydantic_partials import AutoPartialModel, Missing\n\nclass MyModel(AutoPartialModel):\n some_attr: str\n another_field: str\n\n# By default, automatic defined partial fields without any value will get set to a\n# special `Missing` type. Any field that is set to Missing is\n# excluded from the model_dump/model_dump_json.\nobj = MyModel()\nassert obj.some_attr is Missing\nassert obj.model_dump() == {}\n\n# You can set the real value at any time, and it will behave like expected.\nobj.some_attr = 'hello'\nassert obj.some_attr is 'hello'\nassert obj.model_dump() == {'some_attr': 'hello'}\n\n# You can always manually set a field to `Missing` directly.\nobj.some_attr = Missing\n\n# And now it's removed from the model-dump.\nassert obj.model_dump() == {}\n\n# The json dump is also affected in the same way.\nassert obj.model_dump_json() == '{}'\n\n# Any non-missing fields will be included when dumping/serializing model.\nobj.another_field = 'assigned-value'\n\n# After dumping again, we have `another_field` outputted.\n# The `some_attr` field is not present since it's still `Missing`.\nassert obj.model_dump() == {'another_field': 'assigned-value'}\n```\n\nBy default, all fields without a default value will have the ability to be partial,\nand can be missing from both validation and serialization.\nThis includes any inherited Pydantic fields (from a superclass).\n\n## More Details\n\n### Inheritable\n\nWith `AutoPartialModel`, you can inherit from a model to make an automatic partial-version of the inherited fields:\n\n```python\nfrom pydantic_partials import AutoPartialModel, Missing\nfrom pydantic import ValidationError, BaseModel\n\nclass TestModel(BaseModel):\n name: str\n value: str\n some_null_by_default_field: str | None = None\n\ntry:\n # This should produce an error because\n # `name` and `value`are required fields.\n TestModel()\nexcept ValidationError as e:\n print(f'Pydantic will state `name` + `value` are required: {e}')\nelse:\n raise Exception('Pydantic should have required `required_decimal`.')\n\n # We inherit from `TestModel` and add `PartialModel` to the mix.\n\nclass PartialTestModel(AutoPartialModel, TestModel):\n pass\n\n# `PartialTestModel` can now be allocated without the required fields.\n# Any missing required fields will be marked with the `Missing` value\n# and won't be serialized out.\nobj = PartialTestModel(name='a-name')\n\nassert obj.name == 'a-name'\nassert obj.value is Missing\nassert obj.some_null_by_default_field is None\n\n# The `None` field value is still serialized out,\n# only fields with a `Missing` value assigned are skipped.\nassert obj.model_dump() == {\n 'name': 'a-name', 'some_null_by_default_field': None\n}\n```\n\nNotice that if a field has a default value, it's used instead of marking it as `Missing`.\n\nAlso, the `Missing` sentinel value is a separate value vs `None`, allowing one to easily\nknow if a value is truly just missing or is `None`/`Null`.\n\n### Exclude Fields from Automatic Partials (AutoPartialModel)\n\nYou can exclude specific fields from the automatic partials via these means:\n\n- `AutoPartialExclude[...]`\n - This puts a special `Annotated` item on field to mark it as excluded.\n- `class PartialRequired(PartialModel, auto_partials_exclude={'id', 'created_at'}):`\n - This way provides them via class argument `auto_partials_exclude`\n- Or via the standard `model_config`\n - `model_config = {'auto_partials_exclude': {'id', 'created_at'}}`\n - A dict, using `auto_partials_exclude` as the key and a set of field names as the value.\n\nAny of these methods are inheritable.\nYou can override an excluded value by explicitly marking a field as Partial via `some_field: Partial[str]`\n\nHere is an example using the `AutoPartialExclude` method, also showing how it can inherit.\n\n```python\nfrom pydantic_partials import AutoPartialModel, AutoPartialExclude, Missing\nfrom pydantic import BaseModel, ValidationError\nfrom datetime import datetime\nimport pytest\n\nclass PartialRequired(AutoPartialModel):\n id: AutoPartialExclude[str]\n created_at: AutoPartialExclude[datetime]\n\nclass TestModel(BaseModel):\n id: str\n created_at: datetime\n name: str\n value: str\n some_null_by_default_field: str | None = None\n\nclass PartialTestModel(TestModel, PartialRequired):\n pass\n\n# Will raise validation error for the two fields excluded from auto-partials\nwith pytest.raises(\n ValidationError,\n match=r'2 validation errors[\\w\\W]*'\n r'id[\\w\\W]*Field required[\\w\\W]*'\n r'created_at[\\w\\W]*Field required'\n):\n # This should raise a 'ValidationError'\n PartialTestModel() # type: ignore\n\n# If we give them values, we get no ValidationError\nobj = PartialTestModel(id='some-value', created_at=datetime.now()) # type: ignore\n\n# And fields have the expected values.\nassert obj.id == 'some-value'\nassert obj.name is Missing\n```\n\n### Auto Partials Configuration\n\nNormally you would simply inherit from either `PartialModel` or `AutoPartialModel`, depending on the desired behavior you want.\n\nBut you can also configure the auto-partials aspect via class paramters or the `model_config` attribute:\n\n```python\nfrom pydantic_partials import PartialModel, PartialConfigDict, AutoPartialModel\n\n# `PartialModel` uses `auto_partials` as `False` by default, but we can override that if you want via class argument:\nclass TestModel1(PartialModel, auto_partials=True):\n ...\n\n# Or via `model_config`\n# (PartialConfigDict inherits from Pydantic's `ConfigDict`,\n# so you have all of Pydantic's options still available).\nclass TestModel2(AutoPartialModel):\n model_config = PartialConfigDict(auto_partials=False)\n ...\n```\n\nYou can disable this automatic function. This means you have complete control of exactly which field \ncan be partial or not. You can use either the generic `Partial[...]` generic or a union with `MissingType`\nto mark a field as a partial field. The generic simple makes the union to MissingType for you.\n\n```python\nfrom pydantic_partials import PartialModel, Missing, MissingType, Partial\nfrom decimal import Decimal\nfrom pydantic import ValidationError\n\nclass TestModel(PartialModel):\n # Can use `Partial` generic type\n partial_int: Partial[int] = Missing\n\n # Or union with `MissingType`\n partial_str: str | MissingType\n\n required_decimal: Decimal\n\ntry:\n TestModel()\nexcept ValidationError as e:\n print(f'Pydantic will state `required_decimal` is required: {e}')\nelse:\n raise Exception('Pydantic should have required `required_decimal`.')\n\nobj = TestModel(required_decimal='1.34')\n\n# You can find out at any time if a field is missing or not:\nassert obj.partial_int is Missing\nassert obj.partial_str is Missing\n\nassert obj.required_decimal == Decimal('1.34')\n```\n\n\n[//]: # (--8<-- [end:readme])\n",
"bugtrack_url": null,
"license": null,
"summary": "Pydantic partial model class, with ability to easily dynamically omit fields when serializing a model.",
"version": "2.0.1",
"project_urls": {
"Homepage": "https://github.com/joshorr/pydantic-partials",
"Repository": "https://github.com/joshorr/pydantic-partials"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "972ab059b7d9c51f163929ce85d97889b716ac271c9bf03842c48bb2f53d5805",
"md5": "c1a7626e2c417da4cb701f20c2cc5165",
"sha256": "f5a62d08dd73ecf165cad0825567e684243fed03146337b1dc38271378607c50"
},
"downloads": -1,
"filename": "pydantic_partials-2.0.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "c1a7626e2c417da4cb701f20c2cc5165",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0,>=3.10",
"size": 12484,
"upload_time": "2025-01-31T17:49:30",
"upload_time_iso_8601": "2025-01-31T17:49:30.605142Z",
"url": "https://files.pythonhosted.org/packages/97/2a/b059b7d9c51f163929ce85d97889b716ac271c9bf03842c48bb2f53d5805/pydantic_partials-2.0.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "c8ea54c35bb1e7497881e60ba4ce01aa1a358c9aaf0b277c72523553ad95666a",
"md5": "eb6e38322bd410224109522923c7c8ab",
"sha256": "82e6cb67070fc6b94185f6e48732f5e3eb1eed8ccbea8516a9a21d4da07f81b4"
},
"downloads": -1,
"filename": "pydantic_partials-2.0.1.tar.gz",
"has_sig": false,
"md5_digest": "eb6e38322bd410224109522923c7c8ab",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0,>=3.10",
"size": 13181,
"upload_time": "2025-01-31T17:49:31",
"upload_time_iso_8601": "2025-01-31T17:49:31.592722Z",
"url": "https://files.pythonhosted.org/packages/c8/ea/54c35bb1e7497881e60ba4ce01aa1a358c9aaf0b277c72523553ad95666a/pydantic_partials-2.0.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-01-31 17:49:31",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "joshorr",
"github_project": "pydantic-partials",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "pydantic-partials"
}