Giter Club home page Giter Club logo

pydantic-partial's Introduction

pydantic-partial

Installation

Just use pip install pydantic-partial to install the library.

Note: pydantic-partial is compatible with pydantic versions 1.9, 1.10 and even 2.x (🥳) on Python 3.9, 3.10, 3.11 and 3.12. This is also ensured running all tests on all those versions using tox.

About

Create partial models from your normal pydantic models. Partial models will allow some or all fields to be optional and thus not be required when creating the model instance.

Partial models can be used to support PATCH HTTP requests where the user only wants to update some fields of the model and normal validation for required fields is not required. It may also be used to have partial response DTOs where you want to skip certain fields, this can be useful in combination with exclude_none. It is - like shown in these examples - intended to be used with API use cases, so when using pydantic with for example FastAPI.

Disclaimer: This is still an early release of pydantic-partial. Things might change in the future. PR welcome. ;-)

Usage example

pydantic-partial provides a mixin to generate partial model classes. The mixin can be used like this:

import pydantic
from pydantic_partial import PartialModelMixin

# Something model, then can be used as a partial, too:
class Something(PartialModelMixin, pydantic.BaseModel):
    name: str
    age: int


# Create a full partial model
FullSomethingPartial = Something.model_as_partial()
FullSomethingPartial()  # Same as FullSomethingPartial(name=None, age=None)

Without using the mixin

You also may create partial models without using the mixin:

import pydantic
from pydantic_partial import create_partial_model

# Something model, without the mixin:
class Something(pydantic.BaseModel):
    name: str
    age: int


# Create a full partial model
FullSomethingPartial = create_partial_model(Something)
FullSomethingPartial()  # Same as FullSomethingPartial(name=None, age=None)

Only changing some fields to being optional

pydantic-partial can be used to create partial models that only change some of the fields to being optional. Just pass the list of fields to be optional to the as_partial() or create_partial_model() function.

import pydantic
from pydantic_partial import create_partial_model

class Something(pydantic.BaseModel):
    name: str
    age: int

# Create a partial model only for the name attribute
FullSomethingPartial = create_partial_model(Something, 'name')
FullSomethingPartial(age=40)  # Same as FullSomethingPartial(name=None, age=40)
# This would still raise an error: FullSomethingPartial(age=None, ...)

Recursive partials

Partial models can be created changing the field of all nested models to being optional, too.

from typing import List

import pydantic
from pydantic_partial import PartialModelMixin, create_partial_model

class InnerSomething(PartialModelMixin, pydantic.BaseModel):
    name: str

class OuterSomething(pydantic.BaseModel):
    name: str
    things: List[InnerSomething]

# Create a full partial model
RecursiveOuterSomethingPartial = create_partial_model(OuterSomething, recursive=True)
RecursiveOuterSomethingPartial(things=[
    {},
])

Note: The inner model MUST extend the PartialModelMixin mixin. Otherwise pydantic-partial will not be able to detect which fields may allow to being converted to partial models.

Also note: My recommendation would be to always create such recursive partials by creating partials for all the required models and then override the fields on you outer partial model class. This is way more explicit.

Known limitations

pydantic-partial cannot generate new class types that actually are supported by the Python typing system rules. This means that the partial models will only be recognized as the same as their original model classes - type checkers will not know about the partial model changes and thus will think all those partial fields are still required.

This is due to the fact that Python itself has no concept of partials. pydantic-partial could (in theory) provide plugins for mypy for example to "patch" this in, but this would be a massive amount of work while being kind of a bad hack. The real solution would be to have a partial type in Python itself, but this is not planned for the near future as far as I know.

My recommendation is to use pydantic-partial only for API use cases where you do not need to work with the partial aspects of the models - they are just the DTOs (data transfer objects) you are using. If you need to use partial models in other cases you might get errors by your type checker - if you use one. Please be aware of this.

Note: Not having a good solution in Python itself for this is the reason pydantic does not support partial models in the first place. pydantic-partial is just a really good workaround for this issue.
See issue 2 in this project and issue 1673 in the pydantic project for reference.

Having that all said: If anyone wants to get a working plugin for mypy or others ready, I'm going to very much support this.

Contributing

If you want to contribute to this project, feel free to just fork the project, create a dev branch in your fork and then create a pull request (PR). If you are unsure about whether your changes really suit the project please create an issue first, to talk about this.

pydantic-partial's People

Contributors

ddanier avatar dependabot[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

pydantic-partial's Issues

Decorator for classes

A simple syntax decorator like this would vastly improve this library with how such features are moving towards in modern Python ecosystems.

import pydantic
from uuid import UUID
from pydantic_partial import optional

class Something(pydantic.BaseModel):
    name: str
    age: int

# All these fields are optional
@optional
class SomethingOptional(Something):
    pass


class SomethingId(Something):
    id: UUID

If someone can show me a direction on how to do this, I'm happy to support this ticket.

Feature Request: Completely remove certain fields in the new partial model

Hello, this is a great project, thank you for sharing it, it perfectly accomplishes what I want to do. However, I have a scenario now where I have a base Model with all properties, and I want to convert it into multiple child models with only some properties using 'partial', but currently, I can only make the unnecessary properties optional. It would be perfect if I could completely remove properties in the child models.

Partial classes cannot be used in type annotations

Issue

Using python 3.10.7, pydantic 4.3.2, pydantic-partial 0.3.2 and 0.3.3, mypy 0.971

from pydantic import BaseModel
from pydantic_partial import PartialModelMixin

class Foo(PartialModelMixin, BaseModel):
    id: int

PartialFoo = Foo.as_partial()

reveal_type(Foo())
reveal_type(PartialFoo())

def something(x: PartialFoo):
    return x.dict()

running mypy will return:

tmp/foo.py:10: note: Revealed type is "foo.Foo"
tmp/foo.py:11: note: Revealed type is "foo.Foo"
tmp/foo.py:13: error: Variable "foo.PartialFoo" is not valid as a type
tmp/foo.py:13: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
tmp/foo.py:14: error: PartialFoo? has no attribute "dict"

(revealed type for PartialFoo() is "Any" in 0.3.2 and "foo.Foo" in 0.3.3).

Question / Request

How to use partials as type annotations? If there's a way to do it, could it be documented?

Feature Request: Support custom field validators

Hi!
First of all, thank you for your work, the library provides a really handy way to reduce boilerplate for partial models.

Unfortunately, I faced a blocker for using this library in my case: currently pydantic partial discards metadata from FieldInfo which contains custom validation logic. It would be great if the pydantic-partial library introduced a way to call custom field validators if the value is present.

Here is an example of a possible usage case:

from datetime import timedelta
from typing import Annotated, Any

from pydantic import BaseModel, PlainSerializer, WrapValidator
from pydantic.functional_validators import ModelWrapValidatorHandler
from pydantic_partial import create_partial_model


def timedelta_validator(value: Any, handler: ModelWrapValidatorHandler[timedelta]) -> timedelta:
    if isinstance(value, (timedelta, str)):
        return handler(value)
    elif isinstance(value, (float, int)):
        return timedelta(seconds=value)
    else:
        raise ValueError


TimedeltaSeconds = Annotated[timedelta, PlainSerializer(lambda td: td.total_seconds()), WrapValidator(timedelta_validator)]


class MyModel(BaseModel):
    t: TimedeltaSeconds


MyModelPartial = create_partial_model(MyModel)
assert MyModel(t=timedelta(seconds=10)).model_dump()['t'] == 10.  # ok
assert MyModelPartial(t=timedelta(seconds=10)).model_dump()['t'] == 10.  # error

Thank you!

Version info

pydantic-partial==0.5.2

pydantic version: 2.4.2
pydantic-core version: 2.10.1
pydantic-core build: profile=release pgo=false
python version: 3.11.4 (main, Aug 28 2023, 23:10:28) [Clang 14.0.3 (clang-1403.0.22.14.1)]
platform: macOS-14.0-arm64-arm-64bit
related packages: mypy-1.6.0 typing_extensions-4.8.0 pydantic-settings-2.0.3 fastapi-0.101.1

Feature Request: Specify fields that should remain required

Hi there, this is a really useful project! It would be great if we could pass a list of fields that should remain required in the resulting model—essentially, the opposite of the current ability to specify a subset of fields to be optional.

Unexpected class name returned by representation

A repr call on a regular pydantic model returns a string like ModelName(param=value). For a model created by pydantic_partial.create_partial_model it returns ParentModelNamePartial(param=value) instead and misses the model name.

$ ipython
In [1]: import pydantic, pydantic_partial

In [2]: class Foo(pydantic.BaseModel):
   ...:     a: int
   ...:     b: str
   ...: 

In [3]: repr(Foo(a=1, b="b"))
Out[3]: "Foo(a=1, b='b')"

In [4]: Bar = pydantic_partial.create_partial_model(Foo)

In [5]: repr(Bar(a=1))
Out[5]: "FooPartial(a=1, b=None)"

In [6]: class Baz(Foo):
   ...:     pass
   ...: 

In [7]: repr(Baz(a=1, b="b"))
Out[7]: "Baz(a=1, b='b')"

question

Hey thanks for sharing this project.

I am right now searching for some good way on how to implement a patch approch.
We have a huge datamodel with about 500variables and a tone of nested classes. If I understand it correct I would manually need to setup all the recursive nested classes? Is there a smarter way? For big models it might be to complex

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.