Source code for score.sa.orm.helpers

from .base import IdType, tbl2cls
from sqlalchemy import (
    Column, ForeignKey, Integer, UniqueConstraint)
from sqlalchemy.orm import backref, relationship
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.orderinglist import ordering_list


[docs]def create_relationship_class(cls1, cls2, member, *, classname=None, sorted=False, duplicates=True, backref=None): """ Creates a class linking two given models and adds appropriate relationship properties to the classes. At its minimum, this function requires two classes *cls1* and *cls2* to be linked—where cls1 is assumed to be the owning part of the relation—and the name of the member to be added to the owning class: >>> UserGroup = create_relationship_class(User, Group, 'groups') By default, this will create a class called UserGroup, which looks like the following: >>> class UserGroup(Storable): ... __score_db__: { ... 'inheritance': None ... } ... index = Column(Integer, nullable=False) ... user_id = Column(IdType, nullable=False, ForeignKey('_user.id')) ... user = relationship(Group, foreign_keys=[user_id]) ... group_id = Column(IdType, nullable=False, ForeignKey('_group.id')) ... group = relationship(Group, foreign_keys=[group_id]) You can choose the name of the new class by passing it as the *classname* argument, which also has an effect on the table name. It will also add a new member 'groups' to the User class, which is of type :class:`sqlalchemy.orm.properties.RelationshipProperty`. The parameter *sorted* decides whether the relationship is stored with a sorting 'index' column. It is possible to declare that the relationship does not accept *duplicates*, in which case the table will also have a :class:`UniqueConstraint <sqlalchemy.schema.UniqueConstraint>` on ``[user_id, group_id]`` Providing a *backref* member, will also add a relationship property to the second class with the given name. """ if classname is None: classname = cls1.__name__ + cls2.__name__ idcol1 = cls1.__tablename__[1:] + '_id' idcol2 = cls2.__tablename__[1:] + '_id' refcol1 = cls1.__tablename__[1:] refcol2 = cls2.__tablename__[1:] members = { '__score_db__': { 'inheritance': None }, idcol1: Column( IdType, ForeignKey('%s.id' % cls1.__tablename__, onupdate="CASCADE", ondelete="CASCADE"), nullable=False), idcol2: Column( IdType, ForeignKey('%s.id' % cls2.__tablename__, onupdate="CASCADE", ondelete="CASCADE"), nullable=False), } members[refcol1] = relationship(cls1, foreign_keys=members[idcol1]) members[refcol2] = relationship(cls2, foreign_keys=members[idcol2]) if not duplicates: members['__mapper_args__'] = { 'primary_key': [members[idcol1], members[idcol2]] } if sorted: members['index'] = Column(Integer, nullable=False) cls = type(classname, (cls1.__score_db__['base'],), members) if sorted: rel = relationship(cls2, secondary=cls.__tablename__, order_by='%s.index' % cls.__name__, remote_side=lambda: cls1.id, collection_class=ordering_list('index')) else: rel = relationship(cls2, secondary=cls.__tablename__, remote_side=lambda: cls1.id) setattr(cls1, member, rel) if backref: rel = relationship(cls1, secondary=cls.__tablename__, remote_side=lambda: cls2.id) setattr(cls2, backref, rel) return cls
[docs]def create_collection_class(owner, member, column, *, sorted=True, duplicates=True): """ Creates a class for holding the values of a collection in given *owner* class. The given *owner* class will be updated to have a new *member* with given name, which is a list containing elements as described by *column*: >>> create_collection_class(Group, 'permissions', ... Column(PermissionEnum.db_type(), nullable=False) Group objects will now have a member called 'permissions', which contain a sorted list of PermissionEnum values. See :func:`.create_relationship_class` for the description of the keyword arguments. """ name = owner.__name__ + tbl2cls(member) if sorted: bref = backref(member + '_wrapper', order_by='%s.index' % name, collection_class=ordering_list('index')) else: bref = backref(member + '_wrapper') members = { '__score_db__': { 'inheritance': None }, 'owner_id': Column(IdType, ForeignKey('%s.id' % owner.__tablename__), nullable=False), 'owner': relationship(owner, backref=bref), 'value': column, } if sorted: members['index'] = Column(Integer, nullable=False) if not duplicates: members['__table_args__'] = ( UniqueConstraint(members['owner_id'], column), ) cls = type(name, (owner.__score_db__['base'],), members) proxy = association_proxy(member + '_wrapper', 'value', creator=lambda v: cls(value=v)) setattr(owner, member, proxy) return cls