Pydantic BaseModel with flagged fields by name
Project description
pydantic-flagged
pydantic-flagged is a small extension of Pydantic v2 that introduces the concept of flagged fields. A flagged field is defined by its name matching a condition––for example, ending with an underscore (_). Once flagged fields are identified, you can include or exclude them from serialization with a simple API.
Table of Contents
- Overview
- Installation
- Quick Start
- Why Flagged Fields?
- Customization and Class-Level Configuration
- Nested Models
- Advanced Usage Examples
- Contributing
- License
Overview
By default, pydantic-flagged treats any field whose name ends with an underscore as "flagged." Using flagged fields, you can:
- Exclude them from serialized output (
flagged="exclude") - Include only flagged fields while dropping all others (
flagged="include")
This behavior can be configured and applied to an entire model class, or dynamically at serialization time via model_dump or model_dump_json.
Installation
pip install pydantic-flagged
Make sure you're using Pydantic v2 or higher. If you rely on Pydantic v1, this package will not work as expected.
Quick Start
Use BaseModelFlagged instead of pydantic.BaseModel. For simplicity, let's assume the default flag rule (names ending in _) and see what happens.
from pydantic_flagged import BaseModelFlagged
class MyModel(BaseModelFlagged):
one: int = 0
two_: int = 0 # flagged because it ends with an underscore
print(MyModel().model_dump(flagged="exclude"))
# {'one': 0}
Here, two_ is excluded from the output because we called model_dump(flagged="exclude"). If you switched to flagged="include", only two_ would remain.
Why Flagged Fields?
You might sometimes have fields that you use internally but don’t want to expose externally, or vice versa. Flagging fields and selectively including/excluding them at serialization time can be a convenient way to maintain clarity in your code. While you could accomplish something similar using custom serialization logic, flagged fields make this much simpler and more explicit.
Customization and Class-Level Configuration
pydantic-flagged provides several class-level variables that let you define:
- How a field is flagged (
model_flagged_fields_define) - When flagged fields are included or excluded by default (
model_flagged_fields_ser_mode) - Which context key signals flagged behavior at dump time (
model_flagged_fields_ser_mode_context_key)
Define Your Own Flag Rules
Instead of always relying on the default rule (names ending in _), you can set model_flagged_fields_define to:
- A callable that takes a field name and returns a boolean
- A set or list or tuple of field names (only those in this collection are flagged)
Example:
class MyModel(BaseModelFlagged):
model_flagged_fields_define = ["secret_field", "debug_field"]
secret_field: int = 42
debug_field: str = "verbose logs"
normal_field: bool = True
m = MyModel()
print(m.model_flagged_fields.keys())
# dict_keys(['secret_field', 'debug_field'])
Class-Level Default Serialization Mode
If you want to always exclude flagged fields by default, set:
class Child(BaseModelFlagged):
model_flagged_fields_define = lambda name: name.endswith("_")
model_flagged_fields_ser_mode = "exclude"
one: int = 0
two_: int = 0 # flagged
child = Child()
print(child.model_dump())
# {'one': 0}
Even if you embed Child in a larger model, this class-level setting applies to the Child instance automatically (though you can still override at dump time).
Customization via Context Keys
By default, pydantic-flagged looks for a context key named "flagged" when deciding how to handle flagged fields. If you want to allow multiple different types of flagged models in your hierarchy––all with different rules––you can rename this key per class:
class Color(BaseModelFlagged):
model_flagged_fields_ser_mode_context_key = "color_flagged"
# ...
Then, in your model_dump, you can pass a context dict that contains different keys for each model type:
big_model_instance.model_dump(
context={
"flagged": "exclude", # affects normal flagged classes
"color_flagged": "include" # specifically for Color
}
)
Nested Models
If you embed a BaseModelFlagged subclass inside another model, its class-level default serialization mode (if any) still applies. However, you can override it at the time of serialization by passing either:
- The
flaggedparameter directly (.model_dump(flagged="exclude")), which will override for that specific instance. - A
contextdictionary (.model_dump(context={"flagged": "include"})), which will cascade through nested flagged models that share the samemodel_flagged_fields_ser_mode_context_key.
Example
import pydantic
from pydantic_flagged import BaseModelFlagged
class Child(BaseModelFlagged):
model_flagged_fields_define = lambda name: name.endswith("_")
model_flagged_fields_ser_mode = "exclude"
visible: int = 0
invisible_: int = 0
class Parent(pydantic.BaseModel):
child: Child = Child()
# Default: the child's "exclude" rule hides `invisible_`.
print(Parent().model_dump())
# {'child': {'visible': 0}}
# Override: now we *include* flagged fields for all flagged models in this hierarchy
print(Parent().model_dump(flagged="include"))
# {'child': {'invisible_': 0}}
Advanced Usage Examples
Here are some real-world patterns from the tests:
from pydantic_flagged import BaseModelFlagged
import pydantic
class Point(BaseModelFlagged):
# Exclude flagged by default
model_flagged_fields_define = lambda name: name.endswith("_")
model_flagged_fields_ser_mode = "exclude"
x: int = 0
y_: int = 0 # flagged
class Color(BaseModelFlagged):
# We'll define a custom key for flagged logic
model_flagged_fields_define = lambda name: name.endswith("_")
model_flagged_fields_ser_mode_context_key = "color_flagged"
r: int = 0
g_: int = 0 # flagged
b_: int = 0 # flagged
print(Point().model_dump()) # {'x': 0}
print(Color().model_dump(flagged="include")) # {'g_': 0, 'b_': 0}
class Stuff(pydantic.BaseModel):
point: Point = Point()
color: Color = Color()
# We pass a context that includes instructions for *both* standard flagged fields
# and the "color_flagged" fields.
print(
Stuff().model_dump(
context={
"flagged": "include", # for Point
"color_flagged": "exclude" # for Color
}
)
)
# {'point': {'y_': 0}, 'color': {'r': 0}}
Contributing
Contributions are welcome! Feel free to open issues, suggest ideas, or submit pull requests on GitHub. Please run tests and format your code with black before submitting any PRs.
Running Tests
pytest tests
License
This project is licensed under the MIT License.
© 2023-present Ryan Young. All rights reserved.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pydantic_flagged-0.0.1.tar.gz.
File metadata
- Download URL: pydantic_flagged-0.0.1.tar.gz
- Upload date:
- Size: 10.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7394927bff1612798d12a682760337da68dd2e7ea25aba7f55851d5f29f15109
|
|
| MD5 |
fcdffad8d55a28d64bf6e654ce8026fc
|
|
| BLAKE2b-256 |
a07c4bcbacfc4dcfdfd00b779faa0bce95e9fa10ac8380c430c07a8259a9a6f5
|
File details
Details for the file pydantic_flagged-0.0.1-py2.py3-none-any.whl.
File metadata
- Download URL: pydantic_flagged-0.0.1-py2.py3-none-any.whl
- Upload date:
- Size: 8.4 kB
- Tags: Python 2, Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
27becbc7533909c92218ca4f614b03cf5f0b7d6cb45aecf733b36b94c30b517f
|
|
| MD5 |
276b5bc3f4a10663ec47ead3fc458123
|
|
| BLAKE2b-256 |
baffbcdb6847e16abf2bf47fd6d14504ab7b42ee4730c8380362d4e36f22c63b
|