SQLAlchemy mixin classes

Coaster provides a number of mixin classes for SQLAlchemy models. To use in your Flask app:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from coaster.sqlalchemy import BaseMixin

app = Flask(__name__)
db = SQLAlchemy(app)

class MyModel(BaseMixin, db.Model):
    __tablename__ = 'my_model'

Mixin classes must always appear before db.Model in your model’s base classes.

class coaster.sqlalchemy.mixins.IdMixin[source]

Provides the id primary key column

query_class

alias of Query

url_id[source]

The URL id, integer primary key rendered as a string

class coaster.sqlalchemy.mixins.TimestampMixin[source]

Provides the created_at and updated_at audit timestamps

created_at = Column(None, DateTime(), table=None, nullable=False, default=ColumnDefault(<coaster.sqlalchemy.functions.utcnow at 0x7fc6109b7a50; utcnow>))

Timestamp for when this instance was created, in UTC

query_class

alias of Query

updated_at = Column(None, DateTime(), table=None, nullable=False, onupdate=ColumnDefault(<coaster.sqlalchemy.functions.utcnow at 0x7fc6109b7c90; utcnow>), default=ColumnDefault(<coaster.sqlalchemy.functions.utcnow at 0x7fc6109b7bd0; utcnow>))

Timestamp for when this instance was last updated (via the app), in UTC

class coaster.sqlalchemy.mixins.PermissionMixin[source]

Provides the permissions() method used by BaseMixin and derived classes

permissions(user, inherited=None)[source]

Return permissions available to the given user on this object

class coaster.sqlalchemy.mixins.UrlForMixin[source]

Provides a url_for() method used by BaseMixin-derived classes

url_for(action='view', **kwargs)[source]

Return public URL to this instance for a given action (default ‘view’)

url_for_endpoints = {}

Mapping of {action: (endpoint, {param: attr})}, where attr is a string or tuple of strings. This particular dictionary is only used as a fallback. Each subclass will get its own dictionary.

class coaster.sqlalchemy.mixins.NoIdMixin[source]

Mixin that combines TimestampMixin, PermissionMixin, RoleMixin and UrlForMixin, for use anywhere where the timestamp columns and helper methods are required, but an id column is not.

class coaster.sqlalchemy.mixins.BaseMixin[source]

Base mixin class for all tables that have an id column.

class coaster.sqlalchemy.mixins.BaseNameMixin(*args, **kw)[source]

Base mixin class for named objects

Changed in version 0.5.0: If you used BaseNameMixin in your app before Coaster 0.5.0: name can no longer be a blank string in addition to being non-null. This is configurable and enforced with a SQL CHECK constraint, which needs a database migration:

for tablename in ['named_table1', 'named_table2', ...]:
    # Drop CHECK constraint first in case it was already present
    op.drop_constraint(tablename + '_name_check', tablename)
    # Create CHECK constraint
    op.create_check_constraint(tablename + '_name_check', tablename, "name <> ''")
classmethod get(name)[source]

Get an instance matching the name

make_name(reserved=[])[source]

Autogenerates a name from the title. If the auto-generated name is already in use in this model, make_name() tries again by suffixing numbers starting with 2 until an available name is found.

Parameters:reserved – List or set of reserved names unavailable for use
reserved_names = []

Prevent use of these reserved names

classmethod upsert(name, **fields)[source]

Insert or update an instance

class coaster.sqlalchemy.mixins.BaseScopedNameMixin(*args, **kw)[source]

Base mixin class for named objects within containers. When using this, you must provide an model-level attribute “parent” that is a synonym for the parent object. You must also create a unique constraint on ‘name’ in combination with the parent foreign key. Sample use case in Flask:

class Event(BaseScopedNameMixin, db.Model):
    __tablename__ = 'event'
    organizer_id = db.Column(None, db.ForeignKey('organizer.id'))
    organizer = db.relationship(Organizer)
    parent = db.synonym('organizer')
    __table_args__ = (db.UniqueConstraint('organizer_id', 'name'),)

Changed in version 0.5.0: If you used BaseScopedNameMixin in your app before Coaster 0.5.0: name can no longer be a blank string in addition to being non-null. This is configurable and enforced with a SQL CHECK constraint, which needs a database migration:

for tablename in ['named_table1', 'named_table2', ...]:
    # Drop CHECK constraint first in case it was already present
    op.drop_constraint(tablename + '_name_check', tablename)
    # Create CHECK constraint
    op.create_check_constraint(tablename + '_name_check', tablename, "name <> ''")
classmethod get(parent, name)[source]

Get an instance matching the parent and name

make_name(reserved=[])[source]

Autogenerates a name from the title. If the auto-generated name is already in use in this model, make_name() tries again by suffixing numbers starting with 2 until an available name is found.

permissions(user, inherited=None)[source]

Permissions for this model, plus permissions inherited from the parent.

reserved_names = []

Prevent use of these reserved names

short_title()[source]

Generates an abbreviated title by subtracting the parent’s title from this instance’s title.

classmethod upsert(parent, name, **fields)[source]

Insert or update an instance

class coaster.sqlalchemy.mixins.BaseIdNameMixin(*args, **kw)[source]

Base mixin class for named objects with an id tag.

Changed in version 0.5.0: If you used BaseIdNameMixin in your app before Coaster 0.5.0: name can no longer be a blank string in addition to being non-null. This is configurable and enforced with a SQL CHECK constraint, which needs a database migration:

for tablename in ['named_table1', 'named_table2', ...]:
    # Drop CHECK constraint first in case it was already present
    op.drop_constraint(tablename + '_name_check', tablename)
    # Create CHECK constraint
    op.create_check_constraint(tablename + '_name_check', tablename, "name <> ''")
make_name()[source]

Autogenerates a name from the title

url_id_name

Returns a URL name combining url_id and name in id-name syntax. This property is also available as url_name for legacy reasons.

url_name

Returns a URL name combining url_id and name in id-name syntax. This property is also available as url_name for legacy reasons.

class coaster.sqlalchemy.mixins.BaseScopedIdMixin(*args, **kw)[source]

Base mixin class for objects with an id that is unique within a parent. Implementations must provide a ‘parent’ attribute that is either a relationship or a synonym to a relationship referring to the parent object, and must declare a unique constraint between url_id and the parent. Sample use case in Flask:

class Issue(BaseScopedIdMixin, db.Model):
    __tablename__ = 'issue'
    event_id = db.Column(None, db.ForeignKey('event.id'))
    event = db.relationship(Event)
    parent = db.synonym('event')
    __table_args__ = (db.UniqueConstraint('event_id', 'url_id'),)
classmethod get(parent, url_id)[source]

Get an instance matching the parent and url_id

make_id()[source]

Create a new URL id that is unique to the parent container

permissions(user, inherited=None)[source]

Permissions for this model, plus permissions inherited from the parent.

class coaster.sqlalchemy.mixins.BaseScopedIdNameMixin(*args, **kw)[source]

Base mixin class for named objects with an id tag that is unique within a parent. Implementations must provide a ‘parent’ attribute that is a synonym to the parent relationship, and must declare a unique constraint between url_id and the parent. Sample use case in Flask:

class Event(BaseScopedIdNameMixin, db.Model):
    __tablename__ = 'event'
    organizer_id = db.Column(None, db.ForeignKey('organizer.id'))
    organizer = db.relationship(Organizer)
    parent = db.synonym('organizer')
    __table_args__ = (db.UniqueConstraint('organizer_id', 'url_id'),)

Changed in version 0.5.0: If you used BaseScopedIdNameMixin in your app before Coaster 0.5.0: name can no longer be a blank string in addition to being non-null. This is configurable and enforced with a SQL CHECK constraint, which needs a database migration:

for tablename in ['named_table1', 'named_table2', ...]:
    # Drop CHECK constraint first in case it was already present
    op.drop_constraint(tablename + '_name_check', tablename)
    # Create CHECK constraint
    op.create_check_constraint(tablename + '_name_check', tablename, "name <> ''")
classmethod get(parent, url_id)[source]

Get an instance matching the parent and name

make_name()[source]

Autogenerates a title from the name

url_id_name

Returns a URL name combining url_id and name in id-name syntax

url_name

Returns a URL name combining url_id and name in id-name syntax

class coaster.sqlalchemy.mixins.CoordinatesMixin[source]

Adds latitude and longitude columns with a shorthand coordinates property that returns both.

class coaster.sqlalchemy.mixins.UuidMixin[source]

Provides a uuid attribute that is either a SQL UUID column or an alias to the existing id column if the class uses UUID primary keys. Also provides hybrid properties url_id, buid and suuid that provide hex, BUID and ShortUUID representations of the uuid column.

UuidMixin must appear before other classes in the base class order:

class MyDocument(UuidMixin, BaseMixin, db.Model):
    pass

Compatibility table:

Base class Compatible? Notes
BaseMixin Yes  
BaseIdNameMixin Yes  
BaseNameMixin N/A name is secondary key, not uuid
BaseScopedNameMixin N/A name is secondary key, not uuid
BaseScopedIdMixin No Conflicting url_id attribute
BaseScopedIdNameMixin No Conflicting url_id attribute
buid

URL-friendly UUID representation, using URL-safe Base64 (BUID)

suuid

URL-friendly UUID representation, using ShortUUID

url_id

URL-friendly UUID representation as a hex string

class coaster.sqlalchemy.mixins.RoleMixin[source]

Provides methods for role-based access control.

Subclasses must define a __roles__ dictionary with roles and the attributes they have call, read and write access to:

__roles__ = {
    'role_name': {
        'call': {'meth1', 'meth2'},
        'read': {'attr1', 'attr2'},
        'write': {'attr1', 'attr2'},
        },
    }

The with_roles() decorator is recommended over __roles__.

access_for(roles=None, actor=None, anchors=[])[source]

Return a proxy object that limits read and write access to attributes based on the actor’s roles. If the roles parameter isn’t provided, roles_for() is called with the other parameters:

# This typical call:
obj.access_for(actor=current_auth.actor)
# Is shorthand for:
obj.access_for(roles=obj.roles_for(actor=current_auth.actor))
actors_with(roles)[source]

Return an iterable of all actors who have the specified roles on this object. The iterable may be a list, tuple, set or SQLAlchemy query.

Must be implemented by subclasses.

current_access()[source]

Wraps access_for() with current_auth to return a proxy for the currently authenticated user.

current_roles

InspectableSet containing currently available roles on this object, using current_auth. Use in the view layer to inspect for a role being present:

if obj.current_roles.editor:
pass

{% if obj.current_roles.editor %}…{% endif %}

This property is also available in RoleAccessProxy.

roles_for(actor=None, anchors=())[source]

Return roles available to the given actor or anchors on this object. The data type for both parameters are intentionally undefined here. Subclasses are free to define them in any way appropriate. Actors and anchors are assumed to be valid.

The role all is always granted. If actor is specified, the role auth is granted. If not, anon is granted.

Subclasses overriding roles_for() must always call super() to ensure they are receiving the standard roles. Recommended boilerplate:

def roles_for(self, actor=None, anchors=()):
    roles = super(YourClass, self).roles_for(actor, anchors)
    # 'roles' is a set. Add more roles here
    # ...
    return roles