Skip to main content

Generic nested mutable objects and iterables for sqlalchemy

Project description

SQLAlchemy-Mutable

SQLAlchemy-Mutable provides generic nested mutable objects and iterables for SQLAlchemy. Its primary features are:

  1. Nested mutation tracking
  2. Mutation tracking for iterables (list and dict)
  3. Support for embedded database models
  4. Mutable base for customized mutable classes
  5. Support for converting existing classes to mutable classes

Getting Started

Installation

Install and update using pip:

$ pip install -U sqlalchemy-mutable

Setup

  1. Import classes from sqlalchemy_mutable
from sqlalchemy_mutable import Mutable, MutableType, Query
  1. 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)
  1. Initialize a database column with MutableType (or MutableListType or MutableDictType)
class MyModel(Base):
    # ...
    mutable = Column(MutableType)
  1. Add a query class attribute initialized with a scoped_session object (skip this step if using with Flask-SQLAlchemy)
class MyModel(Base):
    # ...
    query = Query(Session)
  1. Set mutable column to Mutable object
class MyModel(Base):
    # ...
    def __init__(self):
        self.mutable = Mutable()
  1. 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:

  1. Create a new mutable class which inherits from Mutable and the existing class.
  2. Associate the new mutable class with the existing class by registering it using @Mutable.register_tracked_type(<Existing Class>).
  3. Define __init__ for the new mutable class. __init__ takes a source (an instance of the existing class type) and a root (the Mutable instance at the root of the nested mutable structure, default to None). 3.1. Assign the root with self.root=root. 3.2. Collect the arguments and keyworks arguments of the existing class constructor from source. 3.3. Call super().__init__(root, <arguments>) where the arguments following root are 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:

  1. Existing class methods take (potentially) mutable arguments. Convert existing class method arguments to Mutable objects before passing to the existing class method with super().<method>(<converted arguments>). Mutable provides convenience methods for converting arguments: 1.1. _convert(object, root=None) converts a single object. 1.2. _convert_iterable(iterable) converts iterables like list. 1.3. _convert_mapping(mapping) converts key:value mappings like dict.
  2. The existing class contains items other than its attributes whose mutations you want to track. For example, a list contains potentially mutable items which are not attributes. In this case, the new mutable class must have a _tracked_items attribute which lists these items.
  3. 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 call self._changed() in addition to the existing class method super().<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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sqlalchemy-mutable-0.0.1.tar.gz (8.0 kB view details)

Uploaded Source

Built Distribution

sqlalchemy_mutable-0.0.1-py3-none-any.whl (9.4 kB view details)

Uploaded Python 3

File details

Details for the file sqlalchemy-mutable-0.0.1.tar.gz.

File metadata

  • Download URL: sqlalchemy-mutable-0.0.1.tar.gz
  • Upload date:
  • Size: 8.0 kB
  • Tags: Source
  • Uploaded using 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

File hashes

Hashes for sqlalchemy-mutable-0.0.1.tar.gz
Algorithm Hash digest
SHA256 9eb99b8f911ac7aecea09a365e977fd141d8779a875624e72dcdb246637ec645
MD5 3d2fd8f3c6f46f35bf3629d8731f4d4c
BLAKE2b-256 f1ecf100a45640b800aaa3c32db435b39c92919909261b432c42ae0a876cc9a1

See more details on using hashes here.

File details

Details for the file sqlalchemy_mutable-0.0.1-py3-none-any.whl.

File metadata

  • Download URL: sqlalchemy_mutable-0.0.1-py3-none-any.whl
  • Upload date:
  • Size: 9.4 kB
  • Tags: Python 3
  • Uploaded using 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

File hashes

Hashes for sqlalchemy_mutable-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 8d9850f5b4f65822ddde8c833cc0d622b08fb79e971b39c3c672cfcec12b9796
MD5 96d2dbbeb07e0e84fcbf9855c7684ffe
BLAKE2b-256 dc82602f52e8b4409f5d7ab921c74ccf1699f4ce05550b695bc2f9e2e209b5aa

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page