Ejemplo n.º 1
0
def is_mapped_class(cls):
    """Returns ``True`` if and only if the specified SQLAlchemy model class is
    a mapped class.
    """
    try:
        sqlalchemy_inspect(cls)
        return True
    except:
        return False
Ejemplo n.º 2
0
def is_mapped_class(cls):
    """Returns ``True`` if and only if the specified SQLAlchemy model class is
    a mapped class.

    """
    try:
        sqlalchemy_inspect(cls)
        return True
    except:
        return False
Ejemplo n.º 3
0
def is_mapped_class(cls):
    """Returns ``True`` if and only if the specified SQLAlchemy model class is
    a mapped class.

    """
    try:
        sqlalchemy_inspect(cls)
    except NoInspectionAvailable:
        return False
    else:
        return True
Ejemplo n.º 4
0
def is_mapped_class(cls):
    """Returns ``True`` if and only if the specified SQLAlchemy model class is
    a mapped class.

    """
    try:
        sqlalchemy_inspect(cls)
    except NoInspectionAvailable:
        return False
    else:
        return True
Ejemplo n.º 5
0
def foreign_key_columns(model):
    """Returns a list of the :class:`sqlalchemy.Column` objects that contain
    foreign keys for relationships in the specified model class.

    """
    mapper = sqlalchemy_inspect(model)
    return [c for c in mapper.columns if c.foreign_keys]
Ejemplo n.º 6
0
def foreign_key_columns(model):
    """Returns a list of the :class:`sqlalchemy.Column` objects that contain
    foreign keys for relationships in the specified model class.

    """
    mapper = sqlalchemy_inspect(model)
    return [c for c in mapper.columns if c.foreign_keys]
Ejemplo n.º 7
0
def get_related_model(model, relationname):
    """Gets the class of the model to which `model` is related by the attribute
    whose name is `relationname`.

    For example, if we have the model classes ::

        class Person(Base):
            __tablename__ = 'person'
            id = Column(Integer, primary_key=True)
            articles = relationship('Article')

        class Article(Base):
            __tablename__ = 'article'
            id = Column(Integer, primary_key=True)
            author_id = Column(Integer, ForeignKey('person.id'))
            author = relationship('Person')

    then ::

        >>> get_related_model(Person, 'articles')
        <class 'Article'>
        >>> get_related_model(Article, 'author')
        <class 'Person'>

    This function also "sees through" association proxies and returns
    the model of the proxied remote relation.

    """
    mapper = sqlalchemy_inspect(model)
    attribute = mapper.all_orm_descriptors[relationname]
    # HACK This is required for Python 3.3 only. I'm guessing it lazily
    # loads the attribute or something like that.
    hasattr(model, relationname)
    return get_related_model_from_attribute(attribute)
Ejemplo n.º 8
0
def get_related_model(model, relationname):
    """Gets the class of the model to which `model` is related by the attribute
    whose name is `relationname`.

    For example, if we have the model classes ::

        class Person(Base):
            __tablename__ = 'person'
            id = Column(Integer, primary_key=True)
            articles = relationship('Article')

        class Article(Base):
            __tablename__ = 'article'
            id = Column(Integer, primary_key=True)
            author_id = Column(Integer, ForeignKey('person.id'))
            author = relationship('Person')

    then ::

        >>> get_related_model(Person, 'articles')
        <class 'Article'>
        >>> get_related_model(Article, 'author')
        <class 'Person'>

    This function also "sees through" association proxies and returns
    the model of the proxied remote relation.

    """
    mapper = sqlalchemy_inspect(model)
    attribute = mapper.all_orm_descriptors[relationname]
    # HACK This is required for Python 3.3 only. I'm guessing it lazily
    # loads the attribute or something like that.
    hasattr(model, relationname)
    return get_related_model_from_attribute(attribute)
Ejemplo n.º 9
0
def is_like_list(model_or_instance, relationname):
    """Decides whether a relation of a SQLAlchemy model is list-like.

    A relation may be like a list if it behaves like a to-many relation
    (either lazy or eager)

    `model_or_instance` may be either a SQLAlchemy model class or an
    instance of such a class.

    `relationname` is a string naming a relationship of the given
    model or instance.

    """
    # Use Python's built-in inspect module to decide whether the
    # argument is a model or an instance of a model.
    if not inspect.isclass(model_or_instance):
        model = get_model(model_or_instance)
    else:
        model = model_or_instance
    mapper = sqlalchemy_inspect(model)
    relation = mapper.all_orm_descriptors[relationname]
    if isinstance(relation, AssociationProxy):
        relation = relation.local_attr

    return hasattr(relation.property, 'uselist') and relation.property.uselist
Ejemplo n.º 10
0
def primary_key_names(model):
    """Returns a list of all the primary keys for a model.

    The returned list contains the name of each primary key as a string.

    """
    mapper = sqlalchemy_inspect(model)
    return [column.name for column in mapper.primary_key]
Ejemplo n.º 11
0
def primary_key_names(model):
    """Returns a list of all the primary keys for a model.

    The returned list contains the name of each primary key as a string.

    """
    mapper = sqlalchemy_inspect(model)
    return [column.name for column in mapper.primary_key]
Ejemplo n.º 12
0
def has_field(model, fieldname):
    """Returns ``True`` if the `model` has the specified field
    or if it has a settable hybrid property for this field name.
    """
    descriptors_data = sqlalchemy_inspect(model).all_orm_descriptors._data
    if fieldname in descriptors_data and hasattr(descriptors_data[fieldname], 'fset'):
        return getattr(descriptors_data[fieldname], 'fset') is not None
    return hasattr(model, fieldname)
Ejemplo n.º 13
0
def foreign_key_columns(model):
    """Returns a list of the :class:`sqlalchemy.Column` objects that contain
    foreign keys for relationships in the specified model class.

    """
    inspector = sqlalchemy_inspect(model)
    all_columns = inspector.columns
    return [column for column in all_columns if column.foreign_keys]
Ejemplo n.º 14
0
def has_field(model, fieldname):
    """Returns ``True`` if the `model` has the specified field or if it has a
    settable hybrid property for this field name.

    """
    descriptors = sqlalchemy_inspect(model).all_orm_descriptors._data
    if fieldname in descriptors and hasattr(descriptors[fieldname], 'fset'):
        return descriptors[fieldname].fset is not None
    return hasattr(model, fieldname)
Ejemplo n.º 15
0
def changes_on_update(model):
    """Returns a best guess at whether the specified SQLAlchemy model class is
    modified on updates.

    We guess whether this happens by checking whether any columns of model have
    the :attr:`sqlalchemy.Column.onupdate` attribute set.

    """
    return any(column.onupdate is not None
               for column in sqlalchemy_inspect(model).columns)
Ejemplo n.º 16
0
def attribute_columns(model) -> List[str]:
    """Returns a list of model's column names that should be considered as attributes."""
    inspected_model = sqlalchemy_inspect(model)
    column_attrs = inspected_model.column_attrs.keys()
    descriptors = inspected_model.all_orm_descriptors.items()
    hybrid_columns = [
        k for k, d in descriptors if d.extension_type == HYBRID_PROPERTY
    ]

    return column_attrs + hybrid_columns
Ejemplo n.º 17
0
def has_field(model, fieldname):
    """Returns ``True`` if the `model` has the specified field or if it has a
    settable hybrid property for this field name.

    """
    if get_db_type(model) == 'mongo':
        return fieldname in model.structure
    descriptors = sqlalchemy_inspect(model).all_orm_descriptors._data
    if fieldname in descriptors and hasattr(descriptors[fieldname], 'fset'):
        return descriptors[fieldname].fset is not None
    return hasattr(model, fieldname)
Ejemplo n.º 18
0
def foreign_key_columns(model):
    """Returns a list of the :class:`sqlalchemy.Column` objects that contain
    foreign keys for relationships in the specified model class.

    """
    try:
        inspector = sqlalchemy_inspect(model)
    except NoInspectionAvailable:
        # Well, the inspection of a model class returns a mapper anyway, so
        # let's just assume the inspection would have returned the mapper.
        inspector = class_mapper(model)
    all_columns = inspector.columns
    return [c for c in all_columns if c.foreign_keys]
Ejemplo n.º 19
0
def foreign_key_columns(model):
    """Returns a list of the :class:`sqlalchemy.Column` objects that contain
    foreign keys for relationships in the specified model class.

    """
    try:
        inspector = sqlalchemy_inspect(model)
    except NoInspectionAvailable:
        # Well, the inspection of a model class returns a mapper anyway, so
        # let's just assume the inspection would have returned the mapper.
        inspector = class_mapper(model)
    all_columns = inspector.columns
    return [c for c in all_columns if c.foreign_keys]
Ejemplo n.º 20
0
def is_relationship(model, fieldname):
    """Decides whether a field is a relationship (as opposed to a
    field).

    `model` is a SQLAlchemy model.

    `fieldname` is a string naming a field of the given model. This
    function returns True if and only if the field is a relationship.

    This function currently does *not* return `True` for association
    proxies.

    """
    mapper = sqlalchemy_inspect(model)
    return fieldname in mapper.relationships
Ejemplo n.º 21
0
def is_relationship(model, fieldname):
    """Decides whether a field is a relationship (as opposed to a
    field).

    `model` is a SQLAlchemy model.

    `fieldname` is a string naming a field of the given model. This
    function returns True if and only if the field is a relationship.

    This function currently does *not* return `True` for association
    proxies.

    """
    mapper = sqlalchemy_inspect(model)
    return fieldname in mapper.relationships
Ejemplo n.º 22
0
def get_relations(model):
    """Returns a list of relation names of `model` (as a list of strings).

    For a relationship via an association proxy, this function shows
    only the remote attribute, not the intermediate relationship. For
    example, if there is a table for ``Article`` and ``Tag`` and a table
    associating the two via a many-to-many relationship, ::

        >>> from sqlalchemy.ext.declarative import declarative_base
        >>>
        >>> Base = declarative_base()
        >>> class Article(Base):
        ...     __tablename__ = 'article'
        ...     id = Column(Integer, primary_key=True)
        ...     tags = association_proxy('articletags', 'tag')
        ...
        >>> class ArticleTag(Base):
        ...     __tablename__ = 'articletag'
        ...     article_id = Column(Integer, ForeignKey('article.id'),
        ...                         primary_key=True)
        ...     article = relationship(Article, backref=backref('articletags'))
        ...     tag_id = Column(Integer, ForeignKey('tag.id'),
        ...                     primary_key=True)
        ...     tag = relationship('Tag')
        ...
        >>> class Tag(Base):
        ...     __tablename__ = 'tag'
        ...     id = Column(Integer, primary_key=True)
        ...     name = Column(Unicode)
        ...
        >>> get_relations(Article)
        ['tags']

    """
    mapper = sqlalchemy_inspect(model)
    # If we didn't have to deal with association proxies, we could just
    # do `return list(mapper.relationships)`, but we want to replace all
    # association attributes with the actual remote attributes, as the
    # user would expect. Therefore, we get a dictionary mapping
    # relationship name to association proxy local attribute name, then
    # replace the key with the value wherever such a key appears in the
    # list of relationships.
    alldescriptors = mapper.all_orm_descriptors.items()
    # TODO In Python 2.7+, this should be a dict comprehension.
    association_proxies = dict((v.local_attr.key, k) for k, v in alldescriptors
                               if isinstance(v, AssociationProxy))
    return [association_proxies.get(r, r) for r in mapper.relationships.keys()]
Ejemplo n.º 23
0
def get_relations(model):
    """Returns a list of relation names of `model` (as a list of strings).

    For a relationship via an association proxy, this function shows
    only the remote attribute, not the intermediate relationship. For
    example, if there is a table for ``Article`` and ``Tag`` and a table
    associating the two via a many-to-many relationship, ::

        >>> from sqlalchemy.ext.declarative import declarative_base
        >>>
        >>> Base = declarative_base()
        >>> class Article(Base):
        ...     __tablename__ = 'article'
        ...     id = Column(Integer, primary_key=True)
        ...     tags = association_proxy('articletags', 'tag')
        ...
        >>> class ArticleTag(Base):
        ...     __tablename__ = 'articletag'
        ...     article_id = Column(Integer, ForeignKey('article.id'),
        ...                         primary_key=True)
        ...     article = relationship(Article, backref=backref('articletags'))
        ...     tag_id = Column(Integer, ForeignKey('tag.id'),
        ...                     primary_key=True)
        ...     tag = relationship('Tag')
        ...
        >>> class Tag(Base):
        ...     __tablename__ = 'tag'
        ...     id = Column(Integer, primary_key=True)
        ...     name = Column(Unicode)
        ...
        >>> get_relations(Article)
        ['tags']

    """
    mapper = sqlalchemy_inspect(model)
    # If we didn't have to deal with association proxies, we could just
    # do `return list(mapper.relationships)`, but we want to replace all
    # association attributes with the actual remote attributes, as the
    # user would expect. Therefore, we get a dictionary mapping
    # relationship name to association proxy local attribute name, then
    # replace the key with the value wherever such a key appears in the
    # list of relationships.
    alldescriptors = mapper.all_orm_descriptors.items()
    # TODO In Python 2.7+, this should be a dict comprehension.
    association_proxies = dict((v.local_attr.key, k) for k, v in alldescriptors
                               if isinstance(v, AssociationProxy))
    return [association_proxies.get(r, r) for r in mapper.relationships.keys()]
Ejemplo n.º 24
0
def to_dict(model, instance, deep=None):
    """change ORM Model instance to python dict
    `instance`: model object
    `deep`: related model dict
    """
    try:
        instance_type = type(instance)
        inspected_instance = sqlalchemy_inspect(instance_type)
        column_attrs = inspected_instance.column_attrs.keys()
    except NoInspectionAvailable as e:
        print e
        return instance
    result = dict((col, getattr(instance, col)) for col in column_attrs
                  if not (col.startswith('__')))
    for key, value in result.items():
        if isinstance(value, (datetime.date, datetime.time)):
            result[key] = value.isoformat()
        elif isinstance(value, uuid.UUID):
            result[key] = str(value)
        elif key not in column_attrs and is_mapped_class(type(value)):
            result[key] = to_dict(model, value)
        else:
            # check alias in `info`
            col = getattr(model, key)
            if hasattr(col, 'info') and col.info.get('alias'):
                alias_dict = col.info['alias']
                result[key] = alias_dict.get(value) or value

    '''
    pacakging related instance
    '''
    deep = deep or {}
    for relation, rdeep in deep.items():
        relatedvalue = getattr(instance, relation)
        if relatedvalue is None:
            result[relation] = None
            continue
        result[relation] = [to_dict(model, inst, rdeep)
                            for inst in relatedvalue]

    return result
Ejemplo n.º 25
0
def has_field(model, fieldname):
    """Returns ``True`` if the `model` has the specified field or if it has a
    settable hybrid property for this field name.

    """
    mapper = sqlalchemy_inspect(model)
    # Get all descriptors, which include columns, relationships, and
    # other things like association proxies and hybrid properties.
    descriptors = mapper.all_orm_descriptors
    if fieldname not in descriptors:
        return False
    field = descriptors[fieldname]
    # First, we check whether `fieldname` specifies a settable hybrid
    # property. This is a bit flimsy: we check whether the `fset`
    # attribute has been set on the `hybrid_property` instance. The
    # `fset` instance attribute is only set if the user defined a hybrid
    # property setter.
    if hasattr(field, 'fset'):
        return field.fset is not None
    # At this point, we simply check that the attribute is not callable.
    return not callable(getattr(model, fieldname))
Ejemplo n.º 26
0
def has_field(model, fieldname):
    """Returns ``True`` if the `model` has the specified field or if it has a
    settable hybrid property for this field name.

    """
    mapper = sqlalchemy_inspect(model)
    # Get all descriptors, which include columns, relationships, and
    # other things like association proxies and hybrid properties.
    descriptors = mapper.all_orm_descriptors
    if fieldname not in descriptors:
        return False
    field = descriptors[fieldname]
    # First, we check whether `fieldname` specifies a settable hybrid
    # property. This is a bit flimsy: we check whether the `fset`
    # attribute has been set on the `hybrid_property` instance. The
    # `fset` instance attribute is only set if the user defined a hybrid
    # property setter.
    if hasattr(field, 'fset'):
        return field.fset is not None
    # At this point, we simply check that the attribute is not callable.
    return not callable(getattr(model, fieldname))
Ejemplo n.º 27
0
def assoc_proxy_scalar_collections(model):
    """Yields the name of each association proxy collection as a string.

    This includes each association proxy that proxies to a scalar
    collection (for example, a list of strings) via an association
    table. It excludes each association proxy that proxies to a
    collection of instances (for example, a to-many relationship) via an
    association object.

    .. seealso::

       :func:`scalar_collection_proxied_relations`

    .. versionadded:: 1.0.0

    """
    mapper = sqlalchemy_inspect(model)
    for k, v in mapper.all_orm_descriptors.items():
        if isinstance(v, AssociationProxy) \
           and not isinstance(v.remote_attr.property, RelationshipProperty) \
           and is_like_list(model, v.local_attr.key):
            yield k
Ejemplo n.º 28
0
def assoc_proxy_scalar_collections(model):
    """Yields the name of each association proxy collection as a string.

    This includes each association proxy that proxies to a scalar
    collection (for example, a list of strings) via an association
    table. It excludes each association proxy that proxies to a
    collection of instances (for example, a to-many relationship) via an
    association object.

    .. seealso::

       :func:`scalar_collection_proxied_relations`

    .. versionadded:: 1.0.0

    """
    mapper = sqlalchemy_inspect(model)
    for k, v in mapper.all_orm_descriptors.items():
        if isinstance(v, AssociationProxy) \
           and not isinstance(v.remote_attr.property, RelationshipProperty) \
           and is_like_list(model, v.local_attr.key):
            yield k
Ejemplo n.º 29
0
def is_like_list(model_or_instance, relationname):
    """Decides whether a relation of a SQLAlchemy model is list-like.

    A relation may be like a list if it behaves like a to-many relation
    (either lazy or eager)

    `model_or_instance` may be either a SQLAlchemy model class or an
    instance of such a class.

    `relationname` is a string naming a relationship of the given
    model or instance.

    """
    # Use Python's built-in inspect module to decide whether the
    # argument is a model or an instance of a model.
    if not inspect.isclass(model_or_instance):
        model = get_model(model_or_instance)
    else:
        model = model_or_instance
    mapper = sqlalchemy_inspect(model)
    relation = mapper.all_orm_descriptors[relationname]
    if isinstance(relation, AssociationProxy):
        relation = relation.local_attr
    return relation.property.uselist
Ejemplo n.º 30
0
def to_dict(instance,
            deep=None,
            exclude=None,
            include=None,
            exclude_relations=None,
            include_relations=None,
            include_methods=None):
    """Returns a dictionary representing the fields of the specified `instance`
    of a SQLAlchemy model.

    The returned dictionary is suitable as an argument to
    :func:`flask.jsonify`; :class:`datetime.date` and :class:`uuid.UUID`
    objects are converted to string representations, so no special JSON encoder
    behavior is required.

    `deep` is a dictionary containing a mapping from a relation name (for a
    relation of `instance`) to either a list or a dictionary. This is a
    recursive structure which represents the `deep` argument when calling
    :func:`!_to_dict` on related instances. When an empty list is encountered,
    :func:`!_to_dict` returns a list of the string representations of the
    related instances.

    If either `include` or `exclude` is not ``None``, exactly one of them must
    be specified. If both are not ``None``, then this function will raise a
    :exc:`ValueError`. `exclude` must be a list of strings specifying the
    columns which will *not* be present in the returned dictionary
    representation of the object (in other words, it is a
    blacklist). Similarly, `include` specifies the only columns which will be
    present in the returned dictionary (in other words, it is a whitelist).

    .. note::

       If `include` is an iterable of length zero (like the empty tuple or the
       empty list), then the returned dictionary will be empty. If `include` is
       ``None``, then the returned dictionary will include all columns not
       excluded by `exclude`.

    `include_relations` is a dictionary mapping strings representing relation
    fields on the specified `instance` to a list of strings representing the
    names of fields on the related model which should be included in the
    returned dictionary; `exclude_relations` is similar.

    `include_methods` is a list mapping strings to method names which will
    be called and their return values added to the returned dictionary.

    """
    if (exclude is not None or exclude_relations is not None) and \
            (include is not None or include_relations is not None):
        raise ValueError('Cannot specify both include and exclude.')
    # create a list of names of columns, including hybrid properties
    instance_type = type(instance)
    columns = []
    try:
        inspected_instance = sqlalchemy_inspect(instance_type)
        column_attrs = inspected_instance.column_attrs.keys()
        descriptors = inspected_instance.all_orm_descriptors.items()
        hybrid_columns = [
            k for k, d in descriptors
            if d.extension_type == hybrid.HYBRID_PROPERTY
            and not (deep and k in deep)
        ]
        columns = column_attrs + hybrid_columns
    except NoInspectionAvailable:
        return instance
    # filter the columns based on exclude and include values
    if exclude is not None:
        columns = (c for c in columns if c not in exclude)
    elif include is not None:
        columns = (c for c in columns if c in include)
    # create a dictionary mapping column name to value
    result = dict((col, getattr(instance, col)) for col in columns
                  if not (col.startswith('__') or col in COLUMN_BLACKLIST))
    # add any included methods
    if include_methods is not None:
        result.update(
            dict((method, getattr(instance, method)())
                 for method in include_methods if not '.' in method))
    # Check for objects in the dictionary that may not be serializable by
    # default. Convert datetime objects to ISO 8601 format, convert UUID
    # objects to hexadecimal strings, etc.
    for key, value in result.items():
        if "password" == key:
            del result["password"]
        elif "current_login_ip" == key:
            del result["current_login_ip"]
        elif "last_login_ip" == key:
            del result["last_login_ip"]
        else:
            if isinstance(value, (datetime.date, datetime.time)):
                result[key] = value.isoformat()
            elif isinstance(value, uuid.UUID):
                result[key] = str(value)
            elif key not in column_attrs and is_mapped_class(type(value)):
                result[key] = to_dict(value)
    # recursively call _to_dict on each of the `deep` relations
    deep = deep or {}
    for relation, rdeep in deep.items():
        # Get the related value so we can see if it is None, a list, a query
        # (as specified by a dynamic relationship loader), or an actual
        # instance of a model.
        relatedvalue = getattr(instance, relation)
        if relatedvalue is None:
            result[relation] = None
            continue
        # Determine the included and excluded fields for the related model.
        newexclude = None
        newinclude = None
        if exclude_relations is not None and relation in exclude_relations:
            newexclude = exclude_relations[relation]
        elif (include_relations is not None and relation in include_relations):
            newinclude = include_relations[relation]
        # Determine the included methods for the related model.
        newmethods = None
        if include_methods is not None:
            newmethods = [
                method.split('.', 1)[1] for method in include_methods
                if method.split('.', 1)[0] == relation
            ]
        if is_like_list(instance, relation):
            result[relation] = [
                to_dict(inst,
                        rdeep,
                        exclude=newexclude,
                        include=newinclude,
                        include_methods=newmethods) for inst in relatedvalue
            ]
            continue
        # If the related value is dynamically loaded, resolve the query to get
        # the single instance.
        if isinstance(relatedvalue, Query):
            relatedvalue = relatedvalue.one()
        result[relation] = to_dict(relatedvalue,
                                   rdeep,
                                   exclude=newexclude,
                                   include=newinclude,
                                   include_methods=newmethods)
    return result
Ejemplo n.º 31
0
def is_mapped_class(cls):
    try:
        sqlalchemy_inspect(cls)
        return True
    except:
        return False
Ejemplo n.º 32
0
def get_relations(model):
    """Yields the name of each relationship of a model as a string.

    For a relationship via an association proxy, this function shows
    only the remote attribute, not the intermediate relationship. For
    example, if there is a table for ``Article`` and ``Tag`` and a table
    associating the two via a many-to-many relationship, ::

        from sqlalchemy import Column
        from sqlalchemy import ForeignKey
        from sqlalchemy import Integer
        from sqlalchemy.ext.declarative import declarative_base
        from sqlalchemy.orm import relationship

        Base = declarative_base()

        class Article(Base):
            __tablename__ = 'article'
            id = Column(Integer, primary_key=True)
            articletags = relationship('ArticleTag')
            tags = association_proxy('articletags', 'tag',
                                     creator=lambda tag: ArticleTag(tag=tag))

        class ArticleTag(Base):
            __tablename__ = 'articletag'
            article_id = Column(Integer, ForeignKey('article.id'),
                                primary_key=True)
            tag_id = Column(Integer, ForeignKey('tag.id'), primary_key=True)
            tag = relationship('Tag')

        class Tag(self.Base):
            __tablename__ = 'tag'
            id = Column(Integer, primary_key=True)

    then this function reveals the ``tags`` proxy::

        >>> list(get_relations(Article))
        ['tags']

    Similarly, for association proxies that proxy to a scalar collection
    via an association table, this will show the related model. For
    example, if there is an association proxy for a scalar collection
    like this::

        from sqlalchemy import Column
        from sqlalchemy import ForeignKey
        from sqlalchemy import Integer
        from sqlalchemy import Table
        from sqlalchemy.ext.declarative import declarative_base
        from sqlalchemy.orm import relationship

        Base = declarative_base()

        class Article(Base):
            __tablename__ = 'article'
            id = Column(Integer, primary_key=True)
            tags = relationship('Tag', secondary=lambda: articletags_table)
            tag_names = association_proxy('tags', 'name',
                                          creator=lambda s: Tag(name=s))

        class Tag(self.Base):
            __tablename__ = 'tag'
            id = Column(Integer, primary_key=True)
            name = Column(Unicode)

        articletags_table = \
            Table('articletags', Base.metadata,
                  Column('article_id', Integer, ForeignKey('article.id'),
                         primary_key=True),
                  Column('tag_id', Integer, ForeignKey('tag.id'),
                         primary_key=True)
            )

    then this function yields only the ``tags`` relationship, not the
    ``tag_names`` attribute::

        >>> list(get_relations(Article))
        ['tags']

    """
    mapper = sqlalchemy_inspect(model)

    # If we didn't have to deal with association proxies, we could just
    # do `return list(mapper.relationships)`.
    #
    # However, we need to deal with (at least) two different usages of
    # association proxies: one in which the proxy is to a scalar
    # collection (like a list of strings) and one in which the proxy is
    # to a collection of instances (like a to-many relationship).
    #
    # First we record each association proxy and the the local attribute
    # through which it proxies. This information is stored in a mapping
    # from local attribute key to proxy name. For example, an
    # association proxy defined like this::
    #
    #     tags = associationproxy('articletags', 'tag')
    #
    # is stored below as a dictionary entry mapping 'articletags' to
    # 'tags'.
    association_proxies = {}
    for k, v in mapper.all_orm_descriptors.items():
        if isinstance(v, AssociationProxy):
            association_proxies[v.local_attr.key] = k

    # Next we determine which association proxies represent scalar
    # collections as opposed to to-many relationships. We need to ignore
    # these.
    scalar_collections = set(assoc_proxy_scalar_collections(model))

    # Finally we find all plain old relationships and all association
    # proxy relationships.
    #
    # If the association proxy is through an association object, we
    # yield that too.
    for r in mapper.relationships.keys():
        yield r
        proxy = association_proxies.get(r)
        if proxy is not None and proxy not in scalar_collections:
                yield association_proxies[r]
Ejemplo n.º 33
0
def get_relations(model):
    """Yields the name of each relationship of a model as a string.

    For a relationship via an association proxy, this function shows
    only the remote attribute, not the intermediate relationship. For
    example, if there is a table for ``Article`` and ``Tag`` and a table
    associating the two via a many-to-many relationship, ::

        from sqlalchemy import Column
        from sqlalchemy import ForeignKey
        from sqlalchemy import Integer
        from sqlalchemy.ext.declarative import declarative_base
        from sqlalchemy.orm import relationship

        Base = declarative_base()

        class Article(Base):
            __tablename__ = 'article'
            id = Column(Integer, primary_key=True)
            articletags = relationship('ArticleTag')
            tags = association_proxy('articletags', 'tag',
                                     creator=lambda tag: ArticleTag(tag=tag))

        class ArticleTag(Base):
            __tablename__ = 'articletag'
            article_id = Column(Integer, ForeignKey('article.id'),
                                primary_key=True)
            tag_id = Column(Integer, ForeignKey('tag.id'), primary_key=True)
            tag = relationship('Tag')

        class Tag(self.Base):
            __tablename__ = 'tag'
            id = Column(Integer, primary_key=True)

    then this function reveals the ``tags`` proxy::

        >>> list(get_relations(Article))
        ['tags']

    Similarly, for association proxies that proxy to a scalar collection
    via an association table, this will show the related model. For
    example, if there is an association proxy for a scalar collection
    like this::

        from sqlalchemy import Column
        from sqlalchemy import ForeignKey
        from sqlalchemy import Integer
        from sqlalchemy import Table
        from sqlalchemy.ext.declarative import declarative_base
        from sqlalchemy.orm import relationship

        Base = declarative_base()

        class Article(Base):
            __tablename__ = 'article'
            id = Column(Integer, primary_key=True)
            tags = relationship('Tag', secondary=lambda: articletags_table)
            tag_names = association_proxy('tags', 'name',
                                          creator=lambda s: Tag(name=s))

        class Tag(self.Base):
            __tablename__ = 'tag'
            id = Column(Integer, primary_key=True)
            name = Column(Unicode)

        articletags_table = \
            Table('articletags', Base.metadata,
                  Column('article_id', Integer, ForeignKey('article.id'),
                         primary_key=True),
                  Column('tag_id', Integer, ForeignKey('tag.id'),
                         primary_key=True)
            )

    then this function yields only the ``tags`` relationship, not the
    ``tag_names`` attribute::

        >>> list(get_relations(Article))
        ['tags']

    """
    mapper = sqlalchemy_inspect(model)

    # If we didn't have to deal with association proxies, we could just
    # do `return list(mapper.relationships)`.
    #
    # However, we need to deal with (at least) two different usages of
    # association proxies: one in which the proxy is to a scalar
    # collection (like a list of strings) and one in which the proxy is
    # to a collection of instances (like a to-many relationship).
    #
    # First we record each association proxy and the the local attribute
    # through which it proxies. This information is stored in a mapping
    # from local attribute key to proxy name. For example, an
    # association proxy defined like this::
    #
    #     tags = associationproxy('articletags', 'tag')
    #
    # is stored below as a dictionary entry mapping 'articletags' to
    # 'tags'.
    association_proxies = {}
    for k, v in mapper.all_orm_descriptors.items():
        if isinstance(v, AssociationProxy):
            association_proxies[v.local_attr.key] = k

    # Next we determine which association proxies represent scalar
    # collections as opposed to to-many relationships. We need to ignore
    # these.
    scalar_collections = set(assoc_proxy_scalar_collections(model))

    # Finally we find all plain old relationships and all association
    # proxy relationships.
    #
    # If the association proxy is through an association object, we
    # yield that too.
    for r in mapper.relationships.keys():
        yield r
        proxy = association_proxies.get(r)
        if proxy is not None and proxy not in scalar_collections:
                yield association_proxies[r]
Ejemplo n.º 34
0
def is_mapped_class(cls):
    try:
        sqlalchemy_inspect(cls)
        return True
    except:
        return False
Ejemplo n.º 35
0
def to_dict(instance, deep=None, exclude=None, include=None,
            exclude_relations=None, include_relations=None,
            include_methods=None):
    """Returns a dictionary representing the fields of the specified `instance`
    of a SQLAlchemy model.

    The returned dictionary is suitable as an argument to
    :func:`flask.jsonify`; :class:`datetime.date` and :class:`uuid.UUID`
    objects are converted to string representations, so no special JSON encoder
    behavior is required.

    `deep` is a dictionary containing a mapping from a relation name (for a
    relation of `instance`) to either a list or a dictionary. This is a
    recursive structure which represents the `deep` argument when calling
    :func:`!_to_dict` on related instances. When an empty list is encountered,
    :func:`!_to_dict` returns a list of the string representations of the
    related instances.

    If either `include` or `exclude` is not ``None``, exactly one of them must
    be specified. If both are not ``None``, then this function will raise a
    :exc:`ValueError`. `exclude` must be a list of strings specifying the
    columns which will *not* be present in the returned dictionary
    representation of the object (in other words, it is a
    blacklist). Similarly, `include` specifies the only columns which will be
    present in the returned dictionary (in other words, it is a whitelist).

    .. note::

       If `include` is an iterable of length zero (like the empty tuple or the
       empty list), then the returned dictionary will be empty. If `include` is
       ``None``, then the returned dictionary will include all columns not
       excluded by `exclude`.

    `include_relations` is a dictionary mapping strings representing relation
    fields on the specified `instance` to a list of strings representing the
    names of fields on the related model which should be included in the
    returned dictionary; `exclude_relations` is similar.

    `include_methods` is a list mapping strings to method names which will
    be called and their return values added to the returned dictionary.

    """
    if (exclude is not None or exclude_relations is not None) and \
            (include is not None or include_relations is not None):
        raise ValueError('Cannot specify both include and exclude.')
    # create a list of names of columns, including hybrid properties
    instance_type = type(instance)
    columns = []
    try:
        inspected_instance = sqlalchemy_inspect(instance_type)
        column_attrs = inspected_instance.column_attrs.keys()
        descriptors = inspected_instance.all_orm_descriptors.items()
        hybrid_columns = [k for k, d in descriptors
                          if d.extension_type == hybrid.HYBRID_PROPERTY]
        columns = column_attrs + hybrid_columns
    except NoInspectionAvailable:
        return instance
    # filter the columns based on exclude and include values
    if exclude is not None:
        columns = (c for c in columns if c not in exclude)
    elif include is not None:
        columns = (c for c in columns if c in include)
    # create a dictionary mapping column name to value
    result = dict((col, getattr(instance, col)) for col in columns
                  if not (col.startswith('__') or col in COLUMN_BLACKLIST))
    # add any included methods
    if include_methods is not None:
        result.update(dict((method, getattr(instance, method)())
                           for method in include_methods
                           if not '.' in method))
    # Check for objects in the dictionary that may not be serializable by
    # default. Convert datetime objects to ISO 8601 format, convert UUID
    # objects to hexadecimal strings, etc.
    for key, value in result.items():
        if isinstance(value, (datetime.date, datetime.time)):
            result[key] = value.isoformat()
        elif isinstance(value, uuid.UUID):
            result[key] = str(value)
        elif key not in column_attrs and is_mapped_class(type(value)):
            result[key] = to_dict(value)
    # recursively call _to_dict on each of the `deep` relations
    deep = deep or {}
    for relation, rdeep in deep.items():
        # Get the related value so we can see if it is None, a list, a query
        # (as specified by a dynamic relationship loader), or an actual
        # instance of a model.
        relatedvalue = getattr(instance, relation)
        if relatedvalue is None:
            result[relation] = None
            continue
        # Determine the included and excluded fields for the related model.
        newexclude = None
        newinclude = None
        if exclude_relations is not None and relation in exclude_relations:
            newexclude = exclude_relations[relation]
        elif (include_relations is not None and
              relation in include_relations):
            newinclude = include_relations[relation]
        # Determine the included methods for the related model.
        newmethods = None
        if include_methods is not None:
            newmethods = [method.split('.', 1)[1] for method in include_methods
                          if method.split('.', 1)[0] == relation]
        if is_like_list(instance, relation):
            result[relation] = [to_dict(inst, rdeep, exclude=newexclude,
                                        include=newinclude,
                                        include_methods=newmethods)
                                for inst in relatedvalue]
            continue
        # If the related value is dynamically loaded, resolve the query to get
        # the single instance.
        if isinstance(relatedvalue, Query):
            relatedvalue = relatedvalue.one()
        result[relation] = to_dict(relatedvalue, rdeep, exclude=newexclude,
                                   include=newinclude,
                                   include_methods=newmethods)
    return result
Ejemplo n.º 36
0
    def to_dict(instance, deep=None, exclude=None, include=None,
                exclude_relations=None, include_relations=None,
                include_methods=None):

        COLUMN_BLACKLIST = ('_sa_polymorphic_on', )

        if (exclude is not None or exclude_relations is not None) and \
                (include is not None or include_relations is not None):
            raise ValueError('Cannot specify both include and exclude.')
        # create a list of names of columns, including hybrid properties
        instance_type = type(instance)
        columns = []
        try:
            inspected_instance = sqlalchemy_inspect(instance_type)
            column_attrs = inspected_instance.column_attrs.keys()
            descriptors = inspected_instance.all_orm_descriptors.items()
            hybrid_columns = [k for k, d in descriptors
                              if d.extension_type == hybrid.HYBRID_PROPERTY
                              and not (deep and k in deep)]
            columns = column_attrs + hybrid_columns
        except NoInspectionAvailable:
            return instance
        # filter the columns based on exclude and include values
        if exclude is not None:
            columns = (c for c in columns if c not in exclude)
        elif include is not None:
            columns = (c for c in columns if c in include)
        # create a dictionary mapping column name to value
        result = dict((col, getattr(instance, col)) for col in columns
                      if not (col.startswith('__') or col in COLUMN_BLACKLIST))
        # add any included methods
        if include_methods is not None:
            for method in include_methods:
                if '.' not in method:
                    value = getattr(instance, method)
                    # Allow properties and static attributes in include_methods
                    if callable(value):
                        value = value()
                    result[method] = value
        # Check for objects in the dictionary that may not be serializable by
        # default. Convert datetime objects to ISO 8601 format, convert UUID
        # objects to hexadecimal strings, etc.
        for key, value in result.items():
            if isinstance(value, (datetime.date, datetime.time)):
                result[key] = value.isoformat()
            elif isinstance(value, uuid.UUID):
                result[key] = str(value)
            elif key not in column_attrs and is_mapped_class(type(value)):
                result[key] = instance.to_dict(value)
        # recursively call _to_dict on each of the `deep` relations
        deep = deep or {}
        for relation, rdeep in deep.items():
            # Get the related value so we can see if it is None, a list, a query
            # (as specified by a dynamic relationship loader), or an actual
            # instance of a model.
            relatedvalue = getattr(instance, relation)
            if relatedvalue is None:
                result[relation] = None
                continue
            # Determine the included and excluded fields for the related model.
            newexclude = None
            newinclude = None
            if exclude_relations is not None and relation in exclude_relations:
                newexclude = exclude_relations[relation]
            elif (include_relations is not None and
                  relation in include_relations):
                newinclude = include_relations[relation]
            # Determine the included methods for the related model.
            newmethods = None
            if include_methods is not None:
                newmethods = [method.split('.', 1)[1] for method in include_methods
                              if method.split('.', 1)[0] == relation]
            if is_like_list(instance, relation):
                result[relation] = [instance.to_dict(inst, rdeep, exclude=newexclude,
                                            include=newinclude,
                                            include_methods=newmethods)
                                    for inst in relatedvalue]
                continue
            # If the related value is dynamically loaded, resolve the query to get
            # the single instance.
            if isinstance(relatedvalue, Query):
                relatedvalue = relatedvalue.one()
            result[relation] = instance.to_dict(relatedvalue, rdeep, exclude=newexclude,
                                       include=newinclude,
                                       include_methods=newmethods)
        return result