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 coaster.sqlalchemy.comparators.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 0x7f90f0e2b850; utcnow>))

Timestamp for when this instance was created, in UTC

query_class

alias of coaster.sqlalchemy.comparators.Query

updated_at = Column(None, DateTime(), table=None, nullable=False, onupdate=ColumnDefault(<coaster.sqlalchemy.functions.utcnow at 0x7f90f0e2ba90; utcnow>), default=ColumnDefault(<coaster.sqlalchemy.functions.utcnow at 0x7f90f0e2b9d0; 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

current_permissions

InspectableSet containing currently available permissions from this object, using current_auth.

permissions(actor, 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

classmethod is_url_for(_action, _endpoint=None, _app=None, _external=None, **paramattrs)[source]

View decorator that registers the view as a url_for() target.

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

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

url_for_endpoints = {None: {}}

Mapping of {app: {action: (endpoint, {param: attr})}}, where attr is a string or tuple of strings. The same action can point to different endpoints in different apps. The app may also be None as fallback. Each subclass will get its own dictionary. This particular dictionary is only used as an inherited fallback.

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(actor, 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.

url_name_suuid

Returns a URL name combining name and suuid in name-suuid syntax. To use this, the class must derive from UuidMixin.

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(actor, 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 huuid, buid and suuid that provide hex, URL-safe Base64 and ShortUUID representations of the uuid column.

buid

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

huuid

URL-friendly UUID representation as a hex string

suuid

URL-friendly UUID representation, using ShortUUID

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