Release Notes¶
2.1.0¶
Saffier 2.1.0 expands the ORM into its next layer: engine-pluggable models.
This release keeps the Saffier core as the source of truth while adding an optional adapter layer for external model engines, richer serialization and validation entry points, and substantially expanded documentation for adopting or extending the new architecture.
Added¶
- Engine-pluggable model support through
Registry(model_engine=...)andMeta.model_engine. - Built-in
pydanticandmsgspecmodel engine adapters with lazy imports. - New engine-facing model APIs:
get_model_engine(),get_engine_model_class(),engine_validate(),from_engine(),to_engine_model(),engine_dump(),engine_dump_json(), andengine_json_schema(). - Dedicated model-engine documentation, reference pages, runnable examples, and a custom-engine implementation guide.
Changed¶
- Saffier models remain fully functional with no engine configured; engine support is now a pure opt-in adapter layer on top of the existing ORM behavior.
- Engine selection now participates in model inheritance, proxy-model generation, and registry-level defaults.
- New and touched engine/model code paths now include comprehensive docstrings across both public and internal helpers.
Fixed¶
- Model-engine metadata now propagates correctly through proxy models, inherited models, and copied registries.
- Engine-backed payload generation preserves Saffier-owned serialization semantics while allowing per-engine projection and validation behavior.
2.0.0¶
Saffier 2.0.0 marks the beginning of a new chapter for the project: a sharper, fully modernized ORM with a pure-Python core, stronger query and relationship capabilities, and a cleaner long-term foundation for framework-agnostic async applications.
This release focuses on the areas that matter most in real projects: model behavior, queryset power, relation handling, migration tooling, tenancy support, compatibility, and documentation.
Saffier became faster, better, cleaner. This is the same Saffier you know and love, but with a sharper edge and a stronger core and... a lot of new features and improvements but also... Faster!
Added¶
- QuerySet set operations:
union,union_all,intersect,intersect_all,except_, andexcept_all. - QuerySet
bulk_get_or_createwithbulk_select_or_insertalias. - QuerySet
local_or,batch_size,extra_select, andreference_selectAPIs. - Settings runtime helpers:
configure_settings,reload_settings, andoverride_settings. - Edgy compatibility modules for legacy imports across queryset, tenancy, admin, Lilya middleware, and model helpers.
- Pure-Python
Model.model_json_schema(...)compatibility and richer tenancy helpers such aswith_schema(...)andusing(database=..., schema=...).
Changed¶
- Saffier now ships with a fully pure-Python model/runtime layer and no longer depends on Pydantic internally (Like its sibling Edgy does).
- Query and model
delete()now return deleted row counts. - Documentation tooling now follows the Zensical workflow with Edgy-aligned Hatch/Taskfile docs commands (
docs_prepare,docs_build,docs_clean,serve). - CLI, migration, and application-discovery flows were refreshed for the current runtime and template system.
Fixed¶
- Tenant schema table metadata now keeps foreign-key relationships consistent across related models in non-default schemas.
- QuerySet
raw_delete()added and delete filtering now respects accumulated OR clauses. - Removed
loguru; Saffier now uses Python standard-library logging in core modules. - QuerySet cache parity improved for
all(clear_cache=True), cachedget(), SQL rendering, andselect_for_update(...). - Permission, pagination, lazy-import, and nested
exclude_secrets()compatibility now align more closely with Edgy behavior. - Model save/create extraction now matches Edgy for nullable/default fields, explicit read-only primary-key values, and composite-key / related-field inserts.
1.4.2¶
Changed¶
- Add integration with the newly
databasez0.8.5+. - Internal refactor of the registry.
Fixed¶
- CI integration.
1.4.1¶
Added¶
- Support for
listandtuplesas a type for model_apps.
1.4.0¶
Added¶
- Support for
model_appsinside theMigrateobject allowing global discovery by application. This will make sure all apps will be properly inspected. - Add documentation about the new model_apps.
Changed¶
- Upgrade internal requirements.
1.3.7¶
Changed¶
- New lazy loading settings system making runtime configuration more dynamic and centralized.
1.3.6¶
Changed¶
- Update the internal settings implementation.
1.3.5¶
Changed¶
BREAKING CHANGE
Due to some internal compatibilities, Saffier is rolling back to SAFFIER_SETTINGS_MODULE
from SETTINGS_MODULE
SETTINGS_MODULEwas renamed toSAFFIER_SETTINGS_MODULE.
1.3.4¶
Changed¶
- Update internal anyio dependency.
1.3.3¶
Changed¶
- Upgrade internal requirements.
Fixed¶
auto_nowandauto_now_addonsave()andupdate()wasn't only updating the field withauto_now.- Extraction of the default field for
dateanddatetime.
1.3.2¶
Fixed¶
1.3.1¶
Fixed¶
- Fix default for
SAFFIER_SETTINGS_MODULEif nothing is provided.
1.3.0¶
Added¶
- Added new experimental activate_schema for tenant models using the
usingqueryset operator. - Support for ManyToMany to accept strings to the
toattribute. - Support for new queryset operations only() and defer.
- Intenal
ModelProxyallowing to manipulate objects querysets such asonlyanddefer. - Support for secrets and secret queryset.
Changed¶
- Increased maximum of 63 characters the name of the index/unique.
- ModelRow now contains private methods.
- Updated documentation with missing select_related.
- Updated documentation for access of data via foreign keys.
- Deprecating internal settings from Pydantic in favour of Dymmond Settings.
Breaking changes¶
Saffier now uses Dymmond Settings which this simlpy affects the way the settings module is loaded. Prior to version 1.3.0 it was like this:
SAFFIER_SETTINGS_MODULE=...
From version 1.3.0 is:
SAFFIER_SETTINGS_MODULE=...
The rest remains as it. More information about how to use it in the official documentation.
Fixed¶
- Multiple join tables were not generating the complete join statement when using
select_related. - Fixed metaclass for TenantMixin making sure all the queries are correctly pointing to the right tenant.
- When generating a many to many through model, the maximum length is enforced to be 63 characters.
- Object discovery for intellisense.
- Allow
ManyToManyto also accept a string as a parameter for theto.
1.2.0¶
Added¶
- Support for
syncqueries. This will enable Saffier to run in blocking frameworks like Flask, bottle or any other by using the newly added run_sync.
Fixed¶
- Fixed multi tenancy from contrib.
- Fixed
usingwhere schema name was raising a not found reference for foreign key when querying the tenant.
1.1.0¶
Added¶
- Support for
or_,and_andnot_for SQLAlchemy style queries and Saffier syntax sugar queries.
Changed¶
inspectdbis now handled by an independent isolated calledInspectDB.- Updated internal support for
databasez0.7.0 and this fixes the URL parsing errors for complex passwords caused by theurlsplit.
Fixed¶
server_defaultdoes not raise aValueError.server_defaultadded as validation for nullable.
Warning
This could impact your migrations, so the advise would be to generate a new migration after upgrading to the new version of Saffier to make sure the database reflects the proper nullables/non-nullable fields.
1.0.2¶
Added¶
inspectdballowing to generatesaffier.ReflectModelfrom the database.
Changed¶
- Added
nameforsaffier.UniqueConstraintallowing unique custom names for theunique_together. max_name_lengthin the datastuctures changed to__max_name_length__andClassVar.
1.0.1¶
Changed¶
- Add API Reference.
- Update base requirements.
Fixed¶
Databaseobject docstring.
1.0.0¶
Added¶
- Support for Python 3.12
Changed¶
- Update base requirements.
0.18.0¶
Added¶
- New Prefetch support allowing to simultaneously load nested data onto models.
- New Signal support allowing to "listen" to model events upon actions being triggered.
Changed¶
- Updated pydantic and alembic
0.17.1¶
Fixed¶
- DeclarativeModel generating internal mappings names was breaking for class objects.
0.17.0¶
Added¶
- Multi tenancy support by updating the registry and allowing to create the multi schema.
- Add new
using(schema=...)andusing_with_db(database=..., schema=...)to querysets. - Add support for
create_schemaanddrop_schemavia registry. - Add support to
get_default_schemafrom theregistry.schema. - Documentation for tenancy.
- Improved the documentation for schemas.
- Added a new parameter
extrato registry allowing to pass a Dict like object containing more database connections. This is an alternative to the registries. - Improved documentation for registry explaining how to use the extra parameters. and query them.
- Added a new ConnectionConfig TypedDict for the registry extra.
Changed¶
- Update the
buildforModelandReflectModelto allow passing the schema.
Fixed¶
- Registry
metaclasswasn't reflecting 100% the schema being passed into the metadata and therefore, querying the database public schema.
0.16.0¶
Changed¶
- Updated versions of the requirements to the latest.
- Internal file structure
- Breaking change. Before for fields the import was
from saffier.db.models.fields import ...and that was now changed tofrom saffier.db.fields import ...
Added¶
values()andvalues_list()to the queryset.
Fixed¶
- ConfigDict in settings.
0.15.0¶
Added¶
- SaffierExtra class allowing the use of Saffier tools without depending on the
Migrateobject.
0.14.2¶
Fixed¶
- AsyncIO event loop blocking the reflection.
0.14.1¶
Fixed¶
- Remove super init from
Registry.
0.14.0¶
Changed¶
- Update Saffier core to start using Pydantic 2.0 and improved performance.
Note
This is a massive performance improvement done by Pydantic that is now compiled in Rust. This bring a whole new level of performance to Saffier as well.
Warning
To use this version of Saffier with Ravyn, until it is announced compatibility with pydantic 2.0 with Ravyn, it is recommended to use saffier prior to this release.
0.13.0¶
Changed¶
Before
from saffier import fields
Now
from saffier.db import fields
Added¶
- Added
server_defaultoption for fields allowing to specify if the value should be generated from the DB and how to. - Added support for
save()of the model. PR #62 by @tarsil
0.12.0¶
Added¶
- New version of the
declarative(). PR #60 by @tarsil. ManyToManyandOneToOneadded as alternatives toManyToManyFieldandOneToOneField. The latter will always exist but you can also import theManyToManyandOneToOneas alternative instead.
Fixed¶
- Registry now allowing the
lru_cachingto happen properly.
0.11.0¶
Added¶
declarative()to the models, allowing generating model types of SQLAlchemy declarative base. PR #58 by @tarsil.
0.10.2¶
Added¶
0.10.1¶
Changed¶
0.10.0¶
Changed¶
- Updated to the latest version of pydantic making sure all the fixes are in place.
0.9.0¶
Added¶
- Add protocols to Queryset.
- Add Protocols to Related names
- Support for the new ManyToManyField
- Documentation for ManyToManyField
0.8.0¶
Changed¶
- Updated relationships document with more examples regarding multiple foreign key declarations.
Added¶
containsmethod to queryset allowing to query if a given model or reflected model exists in the queryset.related_nameis now supported onForeignKeyallowing transverse queries.- Allow reverse queries using nested fields.
on_updatefor ForeignKey and OneToOne fields- Multiple ForeignKeys to the same table is now possible.
- Related Name document added
- Nested queries using related_name
0.7.4¶
Fixed¶
- Removed
nested_asynciocausing infinite loops.
0.7.3¶
Added¶
postgresqlTypo in requirement installation.
0.7.2¶
Added¶
db_schema- Added Registry objects of the metadata.
0.7.1¶
Fixed¶
Lifespanevent on shell returning async manager.
0.7.0¶
Changed¶
- Renamed
saffier-admintosaffier. - Deprecate
saffier-admin. Now you can simply callsaffierwith the same commands as before.
Added¶
- New
shellcommand that allows interactive shell with saffier models. - New
SAFFIER_SETTINGS_MODULEallowing to create and pass specific and unique settings to any saffier instance. - Added support for
ipythonandptpythonfor shell access viasaffier.
Fixed¶
- Linting and formatting issues with Ruff.
- Bug with ReflectModel. A ReflectModel might not need all the fields from the database and the mapping should reflect that.
run_until_completeissues fixed withnest_asyncio.
0.6.1¶
Fixed¶
UUIFieldgenerations with Alembic.
0.6.0¶
Added¶
- Support for SQLAlchemy 2.
Changed¶
- Moved from
databasesto its forkdatabasezand updated internal references. DatabaseClientis now being directly used from Databasez test client.
Fixed¶
- Updated requirements.
0.5.0¶
Changed¶
- Updated requirements to support Ravyn >= 1.1.0 for testing.
- Updated testing and docs requirements.
Added¶
- Metaclass option to support database tables reflection. Allowing reading tables from existing database. 35
- Documentation regarding the reflection of tables. #37
Fixed¶
- Typos in documentation
0.4.0¶
Changed¶
Added¶
0.3.0¶
Added¶
-
Integrated the support for native migrations with Saffier.
- This brings native generated migrations within Saffier under Alembic's package, allowing a seemless integration and cross-compatibility with any framework using Saffier.
-
Added new DatabaseTestClient delegating the creating of the
testdatabase for each connection string provided.- No more needed to manually create two separate databases thanks to the client that does the automatic management for you.
0.2.1¶
Changed¶
- This was supposed to go in the release 0.2.0 and it was missed. Updated queryset lookup for functions allowing accesing the model functions from the manager directly.
0.2.0¶
Added¶
- New Index object allowing the creation of internal SQLAlchemy indexes.
Changed¶
- Updated metaclass to validate the fields being added to
indexes.
0.1.0¶
This is the initial release of Saffier.
- Model inheritance - For those cases where you don't want to repeat yourself while maintaining intregity of the models.
- Abstract classes - That's right! Sometimes you simply want a model that holds common fields that doesn't need to created as a table in the database.
- Meta classes - If you are familiar with Django, this is not new to you and Saffier offers this in the same fashion.
- Managers - Versatility at its core, you can have separate managers for your models to optimise specific queries and querysets at ease.
- Filters - Filter by any field you want and need.
- Model operators - Classic operations such as
update,get,get_or_none,bulk_create,bulk_updateand a lot more. - Relationships made it easy - Support for
OneToOneandForeignKeyin the same Django style. - Constraints - Unique constraints through meta fields.