SQLAlchemy-Mutable
SQLAlchemy-Mutable provides generic nested mutable objects and iterables for SQLAlchemy. Its primary features are:
- Nested mutation tracking
- Mutation tracking for iterables (
listanddict) - Support for embedded database models
Mutablebase for customized mutable classes- Support for converting existing classes to mutable classes
Getting Started
Installation
Install and update using pip:
$ pip install -U sqlalchemy-mutable
Setup
- Import classes from sqlalchemy_mutable
from sqlalchemy_mutable import Mutable, MutableType, Query
- Setup the SQLAlchemy session (standard)
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine('sqlite:///:memory:')
session_factory = sessionmaker(bind=engine)
Session = scoped_session(session_factory)
session = Session()
Base = declarative_base()
class MyModel(Base):
__tablename__ = 'mymodel'
id = Column(Integer, primary_key=True)
greeting = Column(String)
- Initialize a database column with
MutableType(orMutableListTypeorMutableDictType)
class MyModel(Base):
# ...
mutable = Column(MutableType)
- Add a
queryclass attribute initialized with ascoped_sessionobject (skip this step if using with Flask-SQLAlchemy)
class MyModel(Base):
# ...
query = Query(Session)
- Set
mutablecolumn toMutableobject
class MyModel(Base):
# ...
def __init__(self):
self.mutable = Mutable()
- Create the database (standard)
Base.metadata.create_all(engine)
Examples
See full examples with SQLAlchemy and Flask-SQLAlchemy
Example 1: Nested mutation tracking
MutableType columns track changes made to nested Mutable objects.
x = MyModel()
session.add(x)
x.mutable.nested_mutable = Mutable()
session.commit()
x.mutable.nested_mutable.greeting = 'hello world'
session.commit()
print(x.mutable.nested_mutable.greeting)
Outputs:
hello world
Example 2: Mutation tracking for iterables
SQLAlchemy-Mutable also supports mutation tracking for nested iterables (list and dict)
x = MyModel()
session.add(x)
x.mutable = {'greeting': []}
session.commit()
x.mutable['greeting'].append('hello world')
session.commit()
print(x.mutable['greeting'][0])
Outputs:
hello world
Example 3: Embedded database models
Database models may be embedded in the Mutable object.
Note: Embedded database models must be flushed or committed before embedding.
x = MyModel()
y = MyModel()
session.add_all([x,y])
session.flush([x,y]) # Flush or commit models before embedding
x.mutable.y = y
session.commit()
y.greeting = 'hello world'
print(x.mutable.y.greeting)
print('Successfully recovered y?', x.mutable.y == y)
Outputs:
hello world
Successfully recovered y? True
Example 4: Mutable base for customized mutable classes
Users can define custom mutable classes by subclassing Mutable.
Note: Custom __init__ function must begin by callingsuper().__init__().
class CustomMutable(Mutable):
def __init__(self, name='world'):
super().__init__() # Begin by calling super().__init__()
self.name = name
def greeting(self):
return 'hello {}'.format(self.name)
x = MyModel()
x.mutable.nested_mutable = CustomMutable()
session.commit()
print(x.mutable.nested_mutable.greeting())
x.mutable.nested_mutable.name = 'moon'
session.commit()
print(x.mutable.nested_mutable.greeting())
Outputs:
hello world
hello moon
Example 5.1: Convert existing classes to mutable classes (basic use)
Users can add mutation tracking to existing classes. The basic steps are:
- Create a new mutable class which inherits from
Mutableand the existing class. - Associate the new mutable class with the existing class by registering it using
@Mutable.register_tracked_type(<Existing Class>). - Define
__init__for the new mutable class.__init__takes asource(an instance of the existing class type) and aroot(theMutableinstance at the root of the nested mutable structure, default toNone). 3.1. Assign the root withself.root=root. 3.2. Collect the arguments and keyworks arguments of the existing class constructor fromsource. 3.3. Callsuper().__init__(root, <arguments>)where the arguments followingrootare those you collected in 3.2. This calls the existing class constructor with the collected arguments.
You can now treat the existing class as if it were mutable.
class ExistingClass():
def __init__(self, name):
self.name = name
print('My name is', self.name)
def greeting(self):
return 'hello {}'.format(self.name)
# 1. Create new mutable class which inherits from Mutable and ExistingClass
# 2. Registration
@Mutable.register_tracked_type(ExistingClass)
class MutableClass(Mutable, ExistingClass):
# 3. Initialization
def __init__(self, source=(), root=None):
self.root = root
src_name = source.name if hasattr(source, 'name') else None
print('source name is', src_name)
super().__init__(root, name=src_name)
x = MyModel()
session.add(x)
x.mutable.nested_mutable = ExistingClass('world')
session.commit()
print(x.mutable.nested_mutable.greeting())
x.mutable.nested_mutable.name = 'moon'
session.commit()
print(x.mutable.nested_mutable.greeting())
Outputs:
My name is world
source name is world
My name is world
hello world
hello moon
Example 5.2: Convert existing classes to mutable classes (advanced use)
Notes for converting more complex existing classes to mutable classes:
- Existing class methods take (potentially) mutable arguments. Convert existing class method arguments to
Mutableobjects before passing to the existing class method withsuper().<method>(<converted arguments>).Mutableprovides convenience methods for converting arguments: 1.1._convert(object, root=None)converts a single object. 1.2._convert_iterable(iterable)converts iterables likelist. 1.3._convert_mapping(mapping)converts key:value mappings likedict. - The existing class contains items other than its attributes whose mutations you want to track. For example, a
listcontains potentially mutable items which are not attributes. In this case, the new mutable class must have a_tracked_itemsattribute which lists these items. - The existing class has methods which mutate the object but do not call
__setattr___,___delattr___,___setitem___, or__delitem__. The new mutable class must redefine these methods to callself._changed()in addition to the existing class methodsuper().<method>().
@Mutable.register_tracked_type(list)
class MutableList(Mutable, list):
def __init__(self, source=(), root=None):
self.root = root
# 1. Convert existing class constructor arguments to Mutable objects
converted_list = self._convert_iterable(source)
super().__init__(root, converted_list)
# 2. Classes with mutable items must have a _tracked_items attribute
# _tracked_items is a list of potentially mutable items
@property
def _tracked_items(self):
return list(self)
# 3. Call self._changed() to register change with the root Mutable object
def append(self, item):
self._changed()
super().append(self._convert(item, self.root))
x = MyModel()
x.mutable.nested_list = []
session.commit()
x.mutable.nested_list.append('hello world')
session.commit()
print(x.mutable.nested_list[0])
Outputs:
hello world
Fortunately, I have already defined MutableList and MutableDict for you. These classes underlie the functionality in Example 2.
License
This project is licensed under the MIT License LICENSE.
Acknowledgements
Much inspiration drawn from SQLAlchemy-JSON
Metadata
Release files for sqlalchemy-mutable 0.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sqlalchemy-mutable-0.0.1.tar.gz | 8.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sqlalchemy_mutable-0.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 17.4 kB
Release files / sqlalchemy-mutable-0.0.1.tar.gz
| Download URL | sqlalchemy-mutable-0.0.1.tar.gz |
|---|---|
| Size | 8.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9eb99b8f911ac7aecea09a365e977fd141d8779a875624e72dcdb246637ec645
|
|
BLAKE2b-256 checksum How to use checksums |
f1ecf100a45640b800aaa3c32db435b39c92919909261b432c42ae0a876cc9a1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/1.14.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/41.2.0 requests-toolbelt/0.9.1 tqdm/4.35.0 CPython/3.7.1
|
Release files / sqlalchemy_mutable-0.0.1-py3-none-any.whl
| Download URL | sqlalchemy_mutable-0.0.1-py3-none-any.whl |
|---|---|
| Size | 9.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8d9850f5b4f65822ddde8c833cc0d622b08fb79e971b39c3c672cfcec12b9796
|
|
BLAKE2b-256 checksum How to use checksums |
dc82602f52e8b4409f5d7ab921c74ccf1699f4ce05550b695bc2f9e2e209b5aa
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/1.14.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/41.2.0 requests-toolbelt/0.9.1 tqdm/4.35.0 CPython/3.7.1
|