- Fix bug in Session/AsynSession methods
first()
,one()
,one_or_none()
,save()
, andsave_all()
that would result insqlalchemy.exc.InvalidRequestError
due tounique()
not being applied to the results. - Add support for Python 3.11 and 3.12.
- Drop support for SQLAlchemy < 2.0. (breaking change)
Update README for v2.
The v2
release is a major rewrite of the library with many incompatibilities and breaking changes from v1. Please see the Migrating to v2.0
section in the docs for details.
- Fix compatibility issues with SQLAlchemy 1.4.
- The following features are incompatible with SQLAlchemy 1.4 and will raise an exception if used:
sqlservice.Query.entities
sqlservice.Query.join_entities
sqlservice.Query.all_entities
sqlservice.SQLClient.prune
- Pin supported SQLAlchemy version to
>=1.0,<1.4
due to incompatibilities with SQAlchemy 1.4.
- Rename
data
argument to_data
inModelBase.__init__()
andModelBase.update()
to avoid conflict when an ORM model has a column attribute named"data"
. - Add official support for Python 3.8.
- Fix issue where all sessions in memory were closed in
SQLClient.disconnect()
. - Add configuration keyword arguments to
SQLClient.__init__()
.
- If a key in
ModelBase.__dict_args__['adapters']
isNone
, then don't serialize that key when callingModel.to_dict()
.
- Fix handling of string keys in
ModelBase.__dict_args__['adapters']
that resulted in an unhandledTypeError
exception in some cases.
- Fix mishandling of case where new mappings passed to
SQLClient.bulk_diff_update()
aren't different than previous mappings.
- Add
SQLClient.bulk_common_update()
andcore.bulk_common_update()
. - Add
SQLClient.bulk_diff_update()
andcore.bulk_diff_update()
. - Move logic in
SQLClient.bulk_insert()
andbulk_insert_many()
tocore.bulk_insert()
andcore.bulk_insert_many()
respectively.
- Minor optimization to
SQLQuery.save()
to not create an intermediate list when saving multiple items.
- Add missing handling for generators in
SQLQuery.save()
.
- Drop support for Python 2.7. (breaking change)
- Don't mutate
models
argument when passed in as a list toSQLClient.save|core.save
. - Allow generators to be passed into
SQLClient.save|core.save
andSQLClient.destroy|core.destroy
. - Remove deprecated methods: (breaking change)
SQLClient.shutdown()
(useSQLClient.disconnect()
)SQLQuery.chain()
SQLQuery.pluck()
SQLQuery.key_by()
SQLQuery.map()
SQLQuery.reduce()
SQLQuery.reduce_right()
SQLQuery.stack_by()
- Add
SQLClient.DEFAULT_CONFIG
class attribute as way to override config defaults at the class level via subclassing. - Rename
SQLClient.shutdown()
todisconnect()
but keepshutdown()
as a deprecated alias. - Deprecate
SQLClient.shutdown()
. UseSQLClient.disconnect()
instead. Will be removed inv1
. - Deprecate
SQLQuery
methods below. Usepydash
library directly or re-implement in subclass ofSQLQuery
and pass toSQLClient()
viaquery_class
argument. Methods will be removed inv1
:SQLQuery.chain()
SQLQuery.pluck()
SQLQuery.key_by()
SQLQuery.map()
SQLQuery.reduce()
SQLQuery.reduce_right()
SQLQuery.stack_by()
- Support Python 3.7.
- Change default behavior of
SQLClient.transaction()
to not override the current session'sautoflush
setting (useSQLClient.transaction(autoflush=True)
instead. (breaking change) - Add boolean
autoflush
option toSQLClient.transaction()
to set session'sautoflush
value for the duration of the transaction. - Add new
sqlservice.event
decorators:on_init_scalar
on_init_collection
on_modified
on_bulk_replace
on_dispose_collection
- Add
SQLClient.ping()
method that performs a basic connection check.
- Add
ModelBase.class_registry()
that returns the declarative class registry from declarative metadata. Roughly equivalent toModelBase._decl_class_registry
but with_sa_*
keys removed. - Pass model instance as third optional argument to
ModelBase.__dict_args__['adapters']
handlers. - Expose default
dict
adapater assqlservice.model.default_dict_adapter
.
- Support model class names as valid keys in
ModelBase.__dict_args__['adapaters']
. Works similar to string namesused insqlalchemy.orm.relationship
. - Support model class orm descriptors (e.g. columns, relationships) as valid keys in
ModelBase.__dict_args__['adapaters']
.
- Remove
readonly
argument fromSQLClient.transaction
and replace with separatecommit
androllback
. (breaking change)- The default is
commit=True
androllback=False
. This behavior mirrors the previous behavior. - When
rollback=True
, thecommit
argument is ignored and the top-level transaction is always rolled back. This is likereadonly=True
in version0.17.0
. - When
commit=False
androllback=False
, the "transaction" isn't finalized and is left open. This is likereadonly=True
in versions<=0.16.1
.
- The default is
- Rollback instead of commit in a readonly transaction issued by
SQLClient.transaction
. (potential breaking change)- There's a potential breaking change for the case where there's nested a write transaction under a readonly transaction. Previously, the write transaction would be committed when the readonly transaction finalized since commit was being called instead of rollback. However with this change, the settings of the first transaction before any nesting will now determine whether the entire transaction is committed or rollbacked.
- Use
repr(self.url)
inSQLClient.__repr__()
instead ofstr()
to mask connection password if provided.
- Support a database URI string as the configuration value for
SQLClient
. For example, previously had to doSQLClient({'SQL_DATABASE_URI': '<db_uri>'})
but now can doSQLClient('<db_uri>')
. - Add
repr()
support toSQLClient
.
- Add
SQL_POOL_PRE_PING
config option toSQLClient
that setspool_pre_ping
argument to engine. Requires SQLAlchemy >= 1.2. Thanks dsully!
- Fix
Query.search()
so thatdict
filter-by criteria will be applied to the base model class of the query if it's set (i.e. makedb.query(ModelA).join(ModelB).search({'a_only_field': 'foo'})
work so that{'a_only_field': 'foo'}
is filtered onModelA.a_only_field
instead ofModelB
). This also applies toQuery.find()
andQuery.find_one()
which usesearch()
internally.
- Fix typo in
SQL_ENCODING
config option mapping to SQLAlchemy parameter. Thanks dsully!
- Make
declarative_base
pass extra keyword arguments tosqlalchemy.ext.declarative.declarative_base
. - Remove
ModelBase.metaclass
andModelBase.metadata
hooks for hoisting those values todeclarative_base()
. Instead, pass optionalmetadata
andmetaclass
arguments directly todeclarative_base
. (breaking change) - Replace broken
declarative_base
decorator usage with new decorator-only function,as_declarative
. Previously,@declarative_base
only worked as a decorator when not "called" (i.e.@declarative_base
worked but@declarative_base(...)
failed).
- Add
ModelBase.__dict_args__
attribute for providing arguments toModelBase.to_dict
. - Add
adapters
option toModelBase.__dict_args__
for mapping model value types to custom serializatoin handlers duringModelBase.to_dict()
call.
- Bump minimum requirement for pydash to
v4.0.1
. - Revert removal of
Query.pluck
but nowpluck
works with a deep path and path list (e.g.['a', 'b', 0, 'c']
to get'value'
in{'a': {'b': [{'c': 'value'}]}}
which is something thatQuery.map
doesn't support.
- Bump minimum requirement for pydash to
v4.0.0
. (breaking change) - Remove
Query.pluck
in favor orQuery.map
sincemap
can do everythingpluck
could. (breaking change) - Rename
Query.index_by
toQuery.key_by
. (breaking change) - Rename
callback
argument toiteratee
forQuery
methods:key_by
stack_by
map
reduce
reduce_right
- Make
SQLClient.save()
update the declarative model registry whenever an model class isn't in it. This allows saving to work when aSQLClient
instance was created before models have been imported yet. - Make
SQLClient.expunge()
support multiple instances. - Make
SQLClient.save()
andSQLQuery.save()
handle saving empty dictionaries.
- Add
engine_options
argument toSQLClient()
to provide additional engine options beyond what is supported by theconfig
argument. - Add
SQLClient.bulk_insert
for performing an INSERT with a multi-row VALUES clause. - Add
SQLClient.bulk_insert_many
for performing anexecutemany()
DBAPI call. - Add additional
SQLClient.session
proxy properties onSQLClient.<proxy>
:bulk_insert_mappings
bulk_save_objects
bulk_update_mappings
is_active
is_modified
no_autoflush
preapre
- Store
SQLClient.models
as a staticdict
instead of computed property but recompute if an attribute error is detected forSQLClient.<Model>
to handle the case of a late model class import. - Fix handling of duplicate base class names during
SQLClient.models
creation for model classes that are defined in different submodules. Previously, duplicate model class names prevented those models from being saved viaSQLClient.save()
.
- Fix handling of
scopefunc
option inSQLClient.create_session
.
- Add
session_class
argument toSQLClient()
to override the default session class used by the session maker. - Add
session_options
argument toSQLClient()
to provide additional session options beyond what is supported by theconfig
argument.
- Rename
sqlservice.Query
toSQLQuery
. (breaking change) - Remove
sqlservice.SQLService
class in favor of utilizingSQLQuery
for thesave
anddestroy
methods for a model class. (breaking change) - Add
SQLQuery.save()
. - Add
SQLQuery.destroy()
. - Add
SQLQuery.model_class
property. - Replace
service_class
argument withquery_class
inSQLClient.__init__()
. (breaking change) - Remove
SQLClient.services
. (breaking change) - When a model class name is used for attribute access on a
SQLClient
instance, return an instance ofSQLQuery(ModelClass)
instead ofSQLService(ModelClass)
. (breaking change)
- Fix passing of
synchronize_session
argument inSQLService.destroy
andSQLClient.destroy
. Argument was mistakenly not being used when calling underlying delete method.
- Add additional database session proxy attributes to
SQLClient
:SQLClient.scalar -> SQLClient.session.scalar
SQLClient.invalidate -> SQLClient.session.invalidate
SQLClient.expire -> SQLClient.session.expire
SQLClient.expire_all -> SQLClient.session.expire_all
SQLClient.expunge -> SQLClient.session.expunge
SQLClient.expunge_all -> SQLClient.session.expunge_all
SQLClient.prune -> SQLClient.session.prune
- Fix compatibility issue with pydash
v3.4.7
.
- Add
core.make_identity
factory function for easily creating basic identity functions from a list of model column objects that can be used withsave()
. - Import
core.save
,core.destroy
,core.transaction
, andcore.make_identity
into make package namespace.
- Fix model instance merging in
core.save
when providing a custom identity function.
- Expose
identity
argument inSQLClient.save
andSQLService.save
.
- Fix bug where the
models
variable was mistakenly redefined during loop iteration incore.save
.
- Add
identity
argument tosave
method to allow a custom identity function to support upserting on something other than just the primary key values. - Make
Query
entity methodsentities
,join_entities
, andall_entities
return entity objects instead of model classes. (breaking change) - Add
Query
methodsmodel_classes
,join_model_classes
, andall_model_classes
return the model classes belonging to a query.
- Fix issue where calling
<Model>.update(data)
did not correctly update a relationship field when both<Model>.<relationship-column>
anddata[<relationship-column>]
were both instances of a model class.
- Allow
Service.find_one
,Service.find
, andQuery.search
to accept a list of lists as the criterion argument. - Rename ModelBase metaclass class attribute from
ModelBase.Meta
toModelBase.metaclass
. (breaking change) - Add support for defining the
metadata
object onModelBase.metadata
and having it used when callingdeclarative_base
. - Add
metadata
andmetaclass
arguments todeclarative_base
that taken precedence over the corresponding class attributes set on the passed in declarative base type. - Rename Model argument/attribute in
SQLClient
to__init__
tomodel_class
. (breaking change) - Remove
Query.top
method. (breaking change) - Proxy
SQLService.__getattr__
togetattr(SQLService.query(), attr)
so thatSQLService
now acts as a proxy to a query instance that uses itsmodel_class
as the primary query entity. - Move
SQLService.find
andSQLService.find_one
toQuery
. - Improve docs.
- Fix issue where updating nested relationship values can lead to conflicting state assertion error in SQLAlchemy's identity map.
- Fix missing
before
andafter
callback argument passing fromcore.save
tocore._add
.
- Fix missing
before
andafter
callback argument passing fromSQLService.save
toSQLClient.save
.
- Add support for
before
andafter
callbacks incore.save
,SQLClient.save
, andSQLService.save
which are invoked before/aftersession.add
is called for each model instance.
- Support additional engine and session configuration values for
SQLClient
.- New engine config options:
SQL_ECHO_POOL
SQL_ENCODING
SQL_CONVERT_UNICODE
SQL_ISOLATION_LEVEL
- New session config options:
SQL_EXPIRE_ON_COMMIT
- New engine config options:
- Add
SQLClient.reflect
method. - Rename
SQLClient.service_registry
andSQLClient.model_registry
toservices
andmodels
. (breaking change) - Support
SQLClient.__getitem__
as proxy toSQLClient.__getattr__
where bothdb[User]
anddb['User']
both map todb.User
. - Add
SQLService.count
method. - Add
Query
methods:index_by
: ConvertsQuery.all()
to adict
of models indexed bycallback
(pydash.index_by)stack_by
: ConvertsQuery.all()
to adict
of lists of models indexed bycallback
(pydash.group_by)map
: MapsQuery.all()
to acallback
(pydash.map_)reduce
: ReducesQuery.all()
throughcallback
(pydash.reduce_)reduce_right
: ReducesQuery.all()
throughcallback
from right (pydash.reduce_right)pluck
: Retrieves value of of specified property from all elements ofQuery.all()
(pydash.pluck)chain
: Initializes a chain object withQuery.all()
(pydash.chain)
- Rename
Query
properties: (breaking change)model_classes
toentities
joined_model_classes
tojoin_entities
all_model_classes
toall_entities
- Add Python 2.7 compatibility.
- Add concept of
model_registry
andservice_registry
toSQLClient
class:SQLClient.model_registry
returns mapping of ORM model names to ORM model classes bound toSQLClient.Model
.SQLService
instances are created with each model class bound to declarative base,SQLClient.Model
and stored inSQLClient.service_registry
.- Access to each model class
SQLService
instance is available via attribute access toSQLClient
. The attribute name corresponds to the model class name (e.g. given aUser
ORM model, it would be accessible atsqlclient.User
.
- Add new methods to
SQLClient
class:save
: Generic saving of model class instances similar toSQLService.save
but works for any model class instance.destroy
: Generic deletion of model class instances ordict
containing primary keys where model class is explicitly passed in. Similar toSQLService.destroy
.
- Rename
SQLService.delete
todestroy
. (breaking change) - Change
SQLService
initialization signature toSQLService(db, model_class)
and remove class attributemodel_class
in favor of instance attribute. (breaking change) - Add properties to
SQLClient
class:service_registry
model_registry
- Add properties to
Query
class:model_classes
: Returns list of model classes used to duringQuery
creation.joined_model_classes
: Returns list of joined model classes ofQuery
.all_model_classes
: ReturnsQuery.model_classes
+Query.joined_model_classes
.
- Remove methods from
SQLService
class: (breaking change)query_one
query_many
default_order_by
(default order by determination moved toQuery.search
)
- Remove
sqlservice.service.transaction
decorator in favor of using transaction context manager within methods. (breaking change) - Fix incorrect passing of
SQL_DATABASE_URI
value toSQLClient.create_engine
inSQLClient.__init__
.
- First release.