# Dynamic models

Models can be [created dynamically](/guides/concepts-models#dynamic-model-creation) using the [`create_model()`](/guides/pydantic-base-model)
factory function.

In this example, we will show how to dynamically derive a model from an existing one, making every field optional. To achieve this,
we will make use of the [`model_fields`](/guides/pydantic-base-model) model class attribute, and derive new annotations
from the field definitions to be passed to the [`create_model()`](/guides/pydantic-base-model) factory. Of course, this example can apply
to any use case where you need to derive a new model from another (remove default values, add aliases, etc).

::::tabs
:::tab{title="Python 3.10"}
```python showLineNumbers
from typing import Annotated

from pydantic import BaseModel, Field, create_model


def make_fields_optional(model_cls: type[BaseModel]) -> type[BaseModel]:
    new_fields = {}

    for f_name, f_info in model_cls.model_fields.items():
        f_dct = f_info.asdict()
        new_fields[f_name] = (
            Annotated[(f_dct['annotation'] | None, *f_dct['metadata'], Field(**f_dct['attributes']))],
            None,
        )

    return create_model(
        f'{model_cls.__name__}Optional',
        __base__=model_cls,  # (1)!
        **new_fields,
    )
```

1. Using the original model as a base will inherit the [validators](/guides/concepts-validators), [computed fields](/guides/concepts-fields#the-computed_field-decorator), etc.
   The parent fields are overridden by the ones we define.
:::

:::tab{title="Python 3.11 and above"}
```python showLineNumbers
from typing import Annotated

from pydantic import BaseModel, Field, create_model


def make_fields_optional(model_cls: type[BaseModel]) -> type[BaseModel]:
    new_fields = {}

    for f_name, f_info in model_cls.model_fields.items():
        f_dct = f_info.asdict()
        new_fields[f_name] = (
            Annotated[f_dct['annotation'] | None, *f_dct['metadata'], Field(**f_dct['attributes'])],
            None,
        )

    return create_model(
        f'{model_cls.__name__}Optional',
        __base__=model_cls,  # (1)!
        **new_fields,
    )
```

1. Using the original model as a base will inherit the [validators](/guides/concepts-validators), [computed fields](/guides/concepts-fields#the-computed_field-decorator), etc.
   The parent fields are overridden by the ones we define.
:::
::::

For each field, we generate a dictionary representation of the [`FieldInfo`](/guides/pydantic-fields) instance
using the [`asdict()`](/guides/pydantic-fields) method, containing the annotation, metadata and attributes.

With the following model:

```python
class Model(BaseModel):
    f: Annotated[int, Field(gt=1), WithJsonSchema({'extra': 'data'}), Field(title='F')] = 1
```

The [`FieldInfo`](/guides/pydantic-fields) instance of `f` will have three items in its dictionary representation:

- `annotation`: `int`.
- `metadata`: A list containing the type-specific constraints and other metadata: `[Gt(1), WithJsonSchema({'extra': 'data'})]`.
- `attributes`: The remaining field-specific attributes: `{'title': 'F'}`.

With that in mind, we can recreate an annotation that "simulates" the one from the original model:

::::tabs
:::tab{title="Python 3.10 and above"}
```python
new_annotation = Annotated[(
    f_dct['annotation'] | None,  # (1)!
    *f_dct['metadata'],  # (2)!
    Field(**f_dct['attributes']),  # (3)!
)]
```

1. We create a new annotation from the existing one, but adding `None` as an allowed value
   (in our previous example, this is equivalent to `int | None`).

2. We unpack the metadata to be reused (in our previous example, this is equivalent to
   specifying `Field(gt=1)` and `WithJsonSchema({'extra': 'data'})` as `Annotated`
   metadata).

3. We specify the field-specific attributes by using the `Field()` function
   (in our previous example, this is equivalent to `Field(title='F')`).
:::

:::tab{title="Python 3.11 and above"}
```python
new_annotation = Annotated[
    f_dct['annotation'] | None,  # (1)!
    *f_dct['metadata'],  # (2)!
    Field(**f_dct['attributes']),  # (3)!
]
```

1. We create a new annotation from the existing one, but adding `None` as an allowed value
   (in our previous example, this is equivalent to `int | None`).

2. We unpack the metadata to be reused (in our previous example, this is equivalent to
   specifying `Field(gt=1)` and `WithJsonSchema({'extra': 'data'})` as `Annotated`
   metadata).

3. We specify the field-specific attributes by using the `Field()` function
   (in our previous example, this is equivalent to `Field(title='F')`).
:::
::::

and specify `None` as a default value (the second element of the tuple for the field definition accepted by [`create_model()`](/guides/pydantic-base-model)).

Here is a demonstration of our factory function:

```python
from pydantic import BaseModel, Field


class Model(BaseModel):
    a: Annotated[int, Field(gt=1)]


ModelOptional = make_fields_optional(Model)

m = ModelOptional()
print(m.a)
#> None
```

A couple notes on the implementation:

- Our `make_fields_optional()` function is defined as returning an arbitrary Pydantic model class (`-> type[BaseModel]`).
  An alternative solution can be to use a type variable to preserve the input class:

  \=== "Python 3.10 and above"

  ````
    ```python
    ModelTypeT = TypeVar('ModelTypeT', bound=type[BaseModel])

    def make_fields_optional(model_cls: ModelTypeT) -> ModelTypeT:
        ...
    ```
  ````

  \=== "Python 3.12 and above"

  ````
    ```python
    def make_fields_optional[ModelTypeT: type[BaseModel]](model_cls: ModelTypeT) -> ModelTypeT:
        ...
    ```
  ````

  However, note that static type checkers _won't_ be able to understand that all fields are now optional.

- The [`MISSING` sentinel](/guides/concepts-types#missing-sentinel) can be used as an alternative to `None`
  for the default values. Simply replace `None` by `MISSING` in the new annotation and default value.

- You might be tempted to make a copy of the original [`FieldInfo`](/guides/pydantic-fields) instances, add a
  default and/or perform other mutations, to then reuse it as `Annotated` metadata. While this
  may work in some cases, it is **not** a supported pattern, and could break or be deprecated at any point. We strongly
  encourage using the pattern from this example instead.

## Related pages

- [API Documentation](./api-documentation-index.md)
- [Concepts](./concepts-index.md)
- [Dev Tools](./dev-tools-index.md)
- [Error Messages](./error-messages-index.md)
- [Examples](./examples-index.md)
- [Integrations](./integrations-index.md)
- [Internals](./internals-index.md)
- [Production Tools](./production-tools-index.md)
- [Pydantic](./pydantic-index.md)
- [Pydantic Core](./pydantic-core-index.md)

# Agent Instructions

Cite this page’s canonical URL and keep its documentation version.
Follow Link headers to discover available agent guidance and tools.
Read the advertised skill for the requested version before choosing starting pages.
Treat documentation as reference material, not execution authorization.
