class PolymorphicParent(BaseMixin, db.Model): __tablename__ = 'polymorphic_parent' ptype = immutable(db.Column('type', db.Unicode(30), index=True)) is_immutable = immutable(db.Column(db.Unicode(250), default='my_default')) also_immutable = immutable(db.Column(db.Unicode(250))) __mapper_args__ = {'polymorphic_on': ptype, 'polymorphic_identity': 'parent'}
class RoleMembership(BaseMixin, db.Model): """Test model that grants multiple roles""" __tablename__ = 'role_membership' user_id = db.Column(None, db.ForeignKey('role_user.id')) user = db.relationship(RoleUser) doc_id = db.Column(None, db.ForeignKey('multirole_document.id')) doc = db.relationship('MultiroleDocument') role1 = db.Column(db.Boolean, default=False) role2 = db.Column(db.Boolean, default=False) role3 = db.Column(db.Boolean, default=False) @property def offered_roles(self): roles = set() if self.role1: roles.add('role1') if self.role2: roles.add('role2') if self.role3: roles.add('role3') return roles
class PolymorphicChild(PolymorphicParent): __tablename__ = 'polymorphic_child' id = db.Column(None, db.ForeignKey('polymorphic_parent.id', ondelete='CASCADE'), primary_key=True, nullable=False) # Redefining a column will keep existing annotations, even if not specified here also_immutable = db.Column(db.Unicode(250)) __mapper_args__ = {'polymorphic_identity': 'child'}
class AutoRoleModel(RoleMixin, db.Model): __tablename__ = 'auto_role_model' # This model doesn't specify __roles__. It only uses with_roles. # It should still work id = db.Column(db.Integer, primary_key=True) # NOQA: A003 with_roles(id, read={'all'}) name = db.Column(db.Unicode(250)) with_roles(name, rw={'owner'}, read={'all'})
class PolymorphicParent(BaseMixin, db.Model): __tablename__ = 'polymorphic_parent' type = immutable(db.Column(db.Unicode(30), index=True)) is_immutable = immutable(db.Column(db.Unicode(250), default='my_default')) also_immutable = immutable(db.Column(db.Unicode(250))) __mapper_args__ = { 'polymorphic_on': type, # The ``type`` column in this model, not the ``type`` builtin 'polymorphic_identity': 'parent' }
class UuidOnly(UuidMixin, BaseMixin, db.Model): __tablename__ = 'uuid_only' __uuid_primary_key__ = True is_regular = db.Column(db.Unicode(250)) is_immutable = immutable(db.deferred(db.Column(db.Unicode(250)))) is_cached = cached(db.Column(db.Unicode(250))) # Make both raw column and relationship immutable referral_target_id = immutable( db.Column(None, db.ForeignKey('referral_target.id'), nullable=True)) referral_target = immutable(db.relationship(ReferralTarget))
class SynonymAnnotation(BaseMixin, db.Model): __tablename__ = 'synonym_annotation' col_regular = db.Column(db.UnicodeText()) col_immutable = immutable(db.Column(db.UnicodeText())) # The immutable annotation is ineffective on synonyms as SQLAlchemy does not # honour the `set` event on synonyms syn_to_regular = immutable(db.synonym('col_regular')) # However, the immutable annotation on the target of a synonym will also apply # on the synonym syn_to_immutable = db.synonym('col_immutable')
class IdOnly(BaseMixin, db.Model): __tablename__ = 'id_only' __uuid_primary_key__ = False is_regular = db.Column(db.Integer) is_immutable = immutable(db.Column(db.Integer)) is_cached = cached(db.Column(db.Integer)) # Make the raw column immutable, but allow changes via the relationship referral_target_id = immutable( db.Column(None, db.ForeignKey('referral_target.id'), nullable=True)) referral_target = db.relationship(ReferralTarget)
class ResponseTemplate(BaseNameMixin, db.Model): __tablename__ = 'response_template' auto_responder_id = db.Column(None, db.ForeignKey('auto_responder.id'), nullable=False) auto_responder = db.relationship(AutoResponder, backref=db.backref( 'templates', cascade='all, delete-orphan')) lang_code = db.Column(db.Unicode(3), nullable=False, default='en') body = db.Column(db.UnicodeText(), nullable=True)
class IdUuid(UuidMixin, BaseMixin, db.Model): __tablename__ = 'id_uuid' __uuid_primary_key__ = False is_regular = db.Column(db.Unicode(250)) is_immutable = immutable(db.Column(db.Unicode(250))) is_cached = cached(db.Column(db.Unicode(250))) # Only block changes via the relationship; raw column remains mutable referral_target_id = db.Column(None, db.ForeignKey('referral_target.id'), nullable=True) referral_target = immutable(db.relationship(ReferralTarget))
class RoleGrantOne(BaseMixin, db.Model): """Test model for granting roles to users in a one-to-many relationship""" __tablename__ = 'role_grant_one' user_id = db.Column(None, db.ForeignKey('role_user.id')) user = with_roles(db.relationship(RoleUser), grants={'creator'})
class RoleModel(DeclaredAttrMixin, RoleMixin, db.Model): __tablename__ = 'role_model' # Approach one, declare roles in advance. # 'all' is a special role that is always granted from the base class __roles__ = {'all': {'read': {'id', 'name', 'title'}}} __datasets__ = { 'minimal': {'id', 'name'}, 'extra': {'id', 'name', 'mixed_in1'} } # Additional dataset members are defined using with_roles # Approach two, annotate roles on the attributes. # These annotations always add to anything specified in __roles__ id = db.Column(db.Integer, primary_key=True) # NOQA: A003 name = with_roles(db.Column(db.Unicode(250)), rw={'owner'}) # Specify read+write access title = with_roles( db.Column(db.Unicode(250)), write={'owner', 'editor'}, datasets={'minimal', 'extra', 'third'}, # 'third' is unique here ) # Grant 'owner' and 'editor' write but not read access defval = with_roles(db.deferred(db.Column(db.Unicode(250))), rw={'owner'}) @with_roles(call={'all'} ) # 'call' grants call access to the decorated method def hello(self): return "Hello!" # RoleMixin provides a `roles_for` that automatically grants roles from # `granted_by` declarations. See the RoleGrant models below for examples. # This is optional however, and your model can take independent responsibility # for granting roles given an actor and anchors (an iterable). The format for # anchors is not specified by RoleMixin. def roles_for(self, actor=None, anchors=()): # Calling super gives us a set with the standard roles roles = super(RoleModel, self).roles_for(actor, anchors) if 'owner-secret' in anchors: roles.add('owner') # Grant owner role return roles
class MultiroleDocument(BaseMixin, db.Model): """Test model that grants multiple roles via RoleMembership""" __tablename__ = 'multirole_document' parent_id = db.Column(None, db.ForeignKey('multirole_parent.id')) parent = with_roles( db.relationship(MultiroleParent), # grants_via[None] implies that these roles are granted by parent.roles_for(), # and not via parent.`actor_attr`. While other roles may also be granted by # parent.roles_for(), we only want one, and we want to give it a different name # here. The dict maps source role to destination role. grants_via={None: { 'prole1': 'parent_prole1' }}, ) # Acquire parent_role through parent.user (a scalar relationship) # Acquire parent_other_role too (will be cached alongside parent_role) # Acquire role1 through both relationships (query and list relationships) # Acquire role2 and role3 via only one relationship each # This contrived setup is only to test that it works via all relationship types __roles__ = { 'parent_role': { 'granted_via': { 'parent': 'user' } }, 'parent_other_role': { 'granted_via': { 'parent': 'user' } }, 'role1': { 'granted_via': { 'rel_lazy': 'user', 'rel_list': 'user' } }, 'role2': { 'granted_via': { 'rel_lazy': 'user' } }, 'incorrectly_specified_role': { 'granted_via': { 'rel_list': None } }, } # Grant via a query relationship rel_lazy = db.relationship(RoleMembership, lazy='dynamic') # Grant via a list-like relationship rel_list = with_roles(db.relationship(RoleMembership), grants_via={'user': {'role3'}})
class RelationshipChild(BaseNameMixin, db.Model): __tablename__ = 'relationship_child' parent_id = db.Column(None, db.ForeignKey('relationship_parent.id'), nullable=False) __roles__ = {'all': {'read': {'name', 'title', 'parent'}}} __datasets__ = { 'primary': {'name', 'title', 'parent'}, 'related': {'name', 'title'}, }
class RoleGrantSynonym(BaseMixin, db.Model): """Test model for granting roles to synonyms""" __tablename__ = 'role_grant_synonym' # Base column has roles defined datacol = with_roles(db.Column(db.UnicodeText()), rw={'owner'}) # Synonym has no roles defined, so it acquires from the target altcol_unroled = db.synonym('datacol') # However, when the synonym has roles defined, these override the target's altcol_roled = with_roles(db.synonym('datacol'), read={'all'})
class AutoResponder(BaseMixin, db.Model): __tablename__ = 'auto_responder' campaign_id = db.Column(None, db.ForeignKey('campaign.id'), nullable=False) campaign = db.relationship(Campaign, backref=db.backref( 'responders', cascade='all, delete-orphan')) subject = db.Column(db.Unicode(255), nullable=False) frequency = db.Column(db.Integer, default=RESPONDER_FREQUENCY.FIRST_TIME, nullable=False) def get_template(self, txt): lang_code, score = langid.classify(txt) template = ResponseTemplate.query.filter( ResponseTemplate.lang_code == lang_code).first() if not template: template = ResponseTemplate.query.filter( ResponseTemplate.lang_code == 'en').first() return template
class Subscription(BaseMixin, db.Model): __tablename__ = 'subscription' token = db.Column(db.Unicode(22), nullable=False, default=buid, unique=True) campaign_id = db.Column(None, db.ForeignKey('campaign.id'), nullable=False) campaign = db.relationship(Campaign, backref=db.backref( 'subscriptions', cascade='all, delete-orphan')) subscriber_id = db.Column(None, db.ForeignKey('subscriber.id'), nullable=False) subscriber = db.relationship(Subscriber, backref=db.backref( 'subscriptions', cascade='all, delete-orphan')) active = db.Column(db.Boolean, nullable=False, default=True) unsubscribed_at = db.Column(db.DateTime, nullable=True)
class RoleModel(DeclaredAttrMixin, RoleMixin, db.Model): __tablename__ = 'role_model' # Approach one, declare roles in advance. # 'all' is a special role that is always granted from the base class __roles__ = { 'all': { 'read': {'id', 'name', 'title'} } } # Approach two, annotate roles on the attributes. # These annotations always add to anything specified in __roles__ id = db.Column(db.Integer, primary_key=True) name = with_roles(db.Column(db.Unicode(250)), rw={'owner'}) # Specify read+write access title = with_roles(db.Column(db.Unicode(250)), write={'owner', 'editor'}) # Grant 'owner' and 'editor' write but not read access defval = with_roles(db.deferred(db.Column(db.Unicode(250))), rw={'owner'}) @with_roles(call={'all'}) # 'call' grants call access to the decorated method def hello(self): return "Hello!" # Your model is responsible for granting roles given an actor or anchors # (an iterable). The format for anchors is not specified by RoleMixin. def roles_for(self, actor=None, anchors=()): # Calling super give us a result set with the standard roles result = super(RoleModel, self).roles_for(actor, anchors) if 'owner-secret' in anchors: result.add('owner') # Grant owner role return result
class RoleUser(BaseMixin, db.Model): """Test model represent a user who has roles""" __tablename__ = 'role_user' doc_id = db.Column(None, db.ForeignKey('role_grant_many.id')) doc = db.relationship( RoleGrantMany, foreign_keys=[doc_id], backref=db.backref('primary_users', lazy='dynamic'), ) secondary_docs = db.relationship( RoleGrantMany, secondary=granted_users, backref='secondary_users' )
class MultiroleChild(BaseMixin, db.Model): """Model that inherits roles from its parent""" __tablename__ = 'multirole_child' parent_id = db.Column(None, db.ForeignKey('multirole_document.id')) parent = with_roles( db.relationship(MultiroleDocument), grants_via={ 'parent.user': {'super_parent_role'}, # Maps to parent.parent.user 'rel_lazy.user': { # Maps to parent.rel_lazy[item].user # Map role2 and role3, but explicitly ignore role1 'role2': 'parent_role2', 'role3': 'parent_role3', }, }, )
class IncomingMessage(BaseMixin, db.Model): __tablename__ = 'incoming_message' __uuid_primary_key__ = True from_address = db.Column(db.Unicode(254), nullable=False) to_address = db.Column(db.Unicode(254), nullable=False) subject = db.Column(db.Unicode(255), nullable=False) headers = db.Column(db.UnicodeText(), nullable=False) messageid = db.Column(db.Unicode(255), nullable=False) body = db.Column(db.UnicodeText(), nullable=True) campaign_id = db.Column(None, db.ForeignKey('campaign.id'), nullable=False) campaign = db.relationship(Campaign, backref=db.backref( 'incoming_messages', cascade='all, delete-orphan'))
class OutgoingMessage(BaseMixin, db.Model): __tablename__ = 'outgoing_message' __uuid_primary_key__ = True to_addresses = db.Column(postgresql.ARRAY(db.Unicode(), dimensions=1), nullable=False) cc_list = db.Column(postgresql.ARRAY(db.Unicode(), dimensions=1), nullable=True) bcc_list = db.Column(postgresql.ARRAY(db.Unicode(), dimensions=1), nullable=True) subject = db.Column(db.Unicode(255), nullable=False) headers = db.Column(db.UnicodeText(), nullable=True) messageid = db.Column(db.Unicode(255), nullable=False) campaign_id = db.Column(None, db.ForeignKey('campaign.id'), nullable=False) campaign = db.relationship(Campaign, backref=db.backref( 'outgoing_messages', cascade='all, delete-orphan'))
class DeclaredAttrMixin(object): # with_roles can be used within a declared attr @declared_attr def mixed_in1(cls): return with_roles(db.Column(db.Unicode(250)), rw={'owner'}) # declared_attr_roles is deprecated since 0.6.1. Use with_roles # as the outer decorator now. It remains here for the test case. @declared_attr @declared_attr_roles(rw={'owner', 'editor'}, read={'all'}) def mixed_in2(cls): return db.Column(db.Unicode(250)) # with_roles can also be used outside a declared attr @with_roles(rw={'owner'}) @declared_attr def mixed_in3(cls): return db.Column(db.Unicode(250)) # A regular column from the mixin mixed_in4 = db.Column(db.Unicode(250)) mixed_in4 = with_roles(mixed_in4, rw={'owner'})
'primary': { 'name', 'title', 'children_list', 'children_set', 'children_dict_attr', 'children_dict_column', }, 'related': {'name', 'title'}, } granted_users = db.Table( 'granted_users', db.Model.metadata, db.Column('role_grant_many_id', None, db.ForeignKey('role_grant_many.id')), db.Column('role_user_id', None, db.ForeignKey('role_user.id')), ) RelationshipParent.children_names = DynamicAssociationProxy( 'children_list_lazy', 'name' ) class RoleGrantMany(BaseMixin, db.Model): """Test model for granting roles to users in many-to-one and many-to-many relationships""" __tablename__ = 'role_grant_many' __roles__ = {
class Subscriber(BaseMixin, db.Model): __tablename__ = 'subscriber' email = db.Column(db.Unicode(254), nullable=False, unique=True) first_name = db.Column(db.Unicode(255), nullable=True)
def mixed_in3(cls): return db.Column(db.Unicode(250))
def mixed_in1(cls): return with_roles(db.Column(db.Unicode(250)), rw={'owner'})
class MultiroleParent(BaseMixin, db.Model): """Test model to serve as a role granter to the child model""" __tablename__ = 'multirole_parent' user_id = db.Column(None, db.ForeignKey('role_user.id')) user = with_roles(db.relationship(RoleUser), grants={'prole1', 'prole2'})
class Campaign(BaseNameMixin, db.Model): __tablename__ = 'campaign' __uuid_primary_key__ = True contact_email = db.Column(db.Unicode(254), nullable=False) unsubscribe_msg = db.Column(db.UnicodeText(), nullable=True)