websauna.system.model.json module

Mutable JSON data support for SQLAlchemy.

Based on the original Kotti CMS implementation:

class websauna.system.model.json.MutationDict(data)[source]

Bases: websauna.system.model.json.WebsaunaFriendlyMutable

http://www.sqlalchemy.org/docs/orm/extensions/mutable.html

clear(*args, **kwargs)
classmethod coerce(key, value)[source]

Given a value, coerce it into the target type.

Can be overridden by custom subclasses to coerce incoming data into a particular type.

By default, raises ValueError.

This method is called in different scenarios depending on if the parent class is of type Mutable or of type MutableComposite. In the case of the former, it is called for both attribute-set operations as well as during ORM loading operations. For the latter, it is only called during attribute-set operations; the mechanics of the composite() construct handle coercion during load operations.

Parameters
  • key – string name of the ORM-mapped attribute being set.

  • value – the incoming value.

Returns

the method should return the coerced value, or raise ValueError if the coercion cannot be completed.

get(*args, **kwargs)
items(*args, **kwargs)
keys(*args, **kwargs)
pop(*args, **kwargs)
setdefault(*args, **kwargs)
update(*args, **kwargs)
values(*args, **kwargs)
class websauna.system.model.json.MutationList(data)[source]

Bases: websauna.system.model.json.WebsaunaFriendlyMutable

append(*args, **kwargs)
clear(*args, **kwargs)
classmethod coerce(key, value)[source]

Given a value, coerce it into the target type.

Can be overridden by custom subclasses to coerce incoming data into a particular type.

By default, raises ValueError.

This method is called in different scenarios depending on if the parent class is of type Mutable or of type MutableComposite. In the case of the former, it is called for both attribute-set operations as well as during ORM loading operations. For the latter, it is only called during attribute-set operations; the mechanics of the composite() construct handle coercion during load operations.

Parameters
  • key – string name of the ORM-mapped attribute being set.

  • value – the incoming value.

Returns

the method should return the coerced value, or raise ValueError if the coercion cannot be completed.

extend(*args, **kwargs)
insert(*args, **kwargs)
pop(*args, **kwargs)
remove(*args, **kwargs)
class websauna.system.model.json.NestedMixin(*args, **kwargs)[source]

Bases: object

Base class to to nested dict and list state tracking.

changed()[source]
try_wrap(value)[source]
class websauna.system.model.json.NestedMutationDict(*args, **kwargs)[source]

Bases: websauna.system.model.json.NestedMixin, websauna.system.model.json.MutationDict

setdefault(key, default)[source]
class websauna.system.model.json.NestedMutationList(*args, **kwargs)[source]

Bases: websauna.system.model.json.NestedMixin, websauna.system.model.json.MutationList

class websauna.system.model.json.WebsaunaFriendlyMutable[source]

Bases: sqlalchemy.ext.mutable.Mutable

A patched mutable that can deal with our generic column types.

classmethod as_mutable(orig_sqltype)[source]

Mark the value as nested mutable value.

What happens here

  • We coerce the return value - the type value set on sqlalchemy.Column() to the underlying SQL typ

  • We mark this type value with a marker attribute

  • Then we set a global SQAlchemy mapper event handler

  • When mapper is done setting up our model classes, it will call the event handler for all models

  • We check if any of the models columns have our marked type value as the value

  • If so we call associate_with_attribute for this model and column that sets up MutableBase._listen_on_attribute event handlers. These event handlers take care of taking the raw dict coming out from database and wrapping it to NestedMutableDict.

Parameters

orig_sqltype – Usually websauna.system.model.column.JSONB instance

Returns

Marked and coerced type value

websauna.system.model.json.init_for_json(cls)[source]

Check if we need to add JSON column specific SQLAlchemy event listers for this model.

websauna.system.model.json.is_json_like_column(c)[source]

Check if the colum.

Return type

bool

websauna.system.model.json.json_serializer(d)[source]

MutationDict friendly json_serializer for create_engine().

See websauna.system.model.meta.get_engine().

http://stackoverflow.com/a/36438671/315168

websauna.system.model.json.setup_default_value_handling(cls)[source]

A SQLAlchemy model class decorator that ensures JSON/JSONB default values are correctly handled.

Settings default values for JSON fields have a bunch of issues, because dict and list instances need to be wrapped in NestedMutationDict and NestedMutationList that correclty set the parent object dirty state if the container content is modified. This class decorator sets up hooks, so that default values are automatically converted to their wrapped versions.

Note

We are only concerned about initial values. When values are loaded from the database, Mutable base class of NestedMutationDict correctly sets up the parent pointers.

Note

This must be applid to the class directly, as SQLAlchemy does not seem ot pick it up if it’s applied to an (abstract) parent class.

This might be addressed in future SQLAlchemy / Websauna versions so that we can get rid of this ugly little decorator.

websauna.system.model.json.wrap_as_nested(key, data, parent)[source]

Convert raw dict or list data to a NestedMutable version with a parent pointer set.

After this is in-place, the object dirty state should be automatically tracked.

Parameters
  • key (str) – Column name

  • data (object) – dict or list

  • parent (object) – SQLAlchemy model instance

Return type

object

Returns

data in the nested mutable wrapper

websauna.system.model.json.wrapper_class

alias of websauna.system.model.json.MutationList