Source code for restfulchemy.convert

"""
    restfulchemy.converter
    ~~~~~~~~~~~~~~~~~~~~~~

    Convert SQLAlchemy models into Marshmallow schemas.

    :copyright: (c) 2016 by Nicholas Repole and contributors.
                See AUTHORS for more details.
    :license: MIT - See LICENSE for more details.
"""
from inflection import camelize
from marshmallow_sqlalchemy.convert import ModelConverter
from restfulchemy.fields import (EmbeddedField, NestedRelated, RelationshipUrl,
                                 APIUrl)


[docs]class ModelResourceConverter(ModelConverter): """Convert a model's fields for use in a `ModelResourceSchema`.""" def _get_field_class_for_property(self, prop): """Determine what class to use for a field based on ``prop``. :param prop: A column property belonging to a sqlalchemy model. :type prop: :class:`~sqlalchemy.orm.properties.ColumnProperty` :return: A field class corresponding to the provided ``prop``. :rtype: type """ if hasattr(prop, 'direction'): if prop.uselist: field_cls = EmbeddedField else: field_cls = EmbeddedField else: column = prop.columns[0] field_cls = self._get_field_class_for_column(column) return field_cls def _add_column_kwargs(self, kwargs, prop): """Update the provided kwargs based on the prop given. :param dict kwargs: A dictionary of kwargs to pass to the eventual field constructor. This argument is modified in place. :param prop: A column property used to determine how ``kwargs`` should be updated. :type prop: :class:`~sqlalchemy.orm.properties.ColumnProperty` """ super(ModelResourceConverter, self)._add_column_kwargs( kwargs, prop.columns[0]) def _add_relationship_kwargs(self, kwargs, prop): """Update the provided kwargs based on the relationship given. :param dict kwargs: A dictionary of kwargs to pass to the eventual field constructor. This argument is modified in place. :param prop: A relationship property used to determine how ``kwargs`` should be updated. :type prop: :class:`~sqlalchemy.orm.properties.RelationshipProperty` """ nullable = True for pair in prop.local_remote_pairs: if not pair[0].nullable: if prop.uselist is True: nullable = False break if prop.uselist: default_field = RelationshipUrl( dump_only=True, resource=prop.mapper.class_.__name__ + 'Resource') embedded_field = NestedRelated( nested=prop.mapper.class_.__name__ + 'Schema', allow_none=nullable, many=prop.uselist ) kwargs.update({ 'default_field': default_field, 'embedded_field': embedded_field, 'embedded': False }) else: default_field = RelationshipUrl( dump_only=True, resource=prop.mapper.class_.__name__ + 'Resource') embedded_field = NestedRelated( nested=prop.mapper.class_.__name__ + 'Schema', allow_none=nullable, many=prop.uselist ) kwargs.update({ 'default_field': default_field, 'embedded_field': embedded_field, 'embedded': False })
[docs] def property2field(self, prop, instance=True, **kwargs): """ :param prop: A column or relationship property used to determine a corresponding field. :type prop: :class:`~sqlalchemy.orm.properties.ColumnProperty` or :class:`~sqlalchemy.orm.properties.RelationshipProperty` :param instance: `True` if this method should return an actual instance of a field, `False` to return the actual field class. :param kwargs: Keyword args to be used in the construction of the field. :return: Depending on the value of ``instance``, either a field or a field class. :rtype: :class:`~marshmallow.fields.Field` or type """ field_class = self._get_field_class_for_property(prop) if not instance: return field_class field_kwargs = self._get_field_kwargs_for_property(prop) field_kwargs.update(kwargs) ret = field_class(**field_kwargs) return ret
def _get_field_kwargs_for_property(self, prop): """Get a dict of kwargs to use for field construction. :param prop: A column or relationship property used to determine what kwargs should be passed to the eventual field constructor. :type prop: :class:`~sqlalchemy.orm.properties.ColumnProperty` or :class:`~sqlalchemy.orm.properties.RelationshipProperty` :return: A dict of kwargs to pass to the eventual field constructor. :rtype: dict """ kwargs = self.get_base_kwargs() if hasattr(prop, 'columns'): self._add_column_kwargs(kwargs, prop) if hasattr(prop, 'direction'): # Relationship property self._add_relationship_kwargs(kwargs, prop) if getattr(prop, 'doc', None): # Useful for documentation generation kwargs['description'] = prop.doc return kwargs
[docs] def fields_for_model(self, model, include_fk=False, fields=None, exclude=None): """Generate fields for the provided model. :param model: The SQLAlchemy model the generated fields correspond to. :param bool include_fk: `True` if fields should be generated for foreign keys, `False` otherwise. :param fields: A collection of field names to generate. :type fields: :class:`~collections.Iterable` or None :param exclude: A collection of field names not to generate. :type exclude: :class:`~collections.Iterable` or None :return: Generated fields corresponding to each model property. :rtype: list """ result = super(ModelResourceConverter, self).fields_for_model( model, include_fk, fields, exclude) result["self"] = APIUrl() return result
[docs]class CamelModelResourceConverter(ModelResourceConverter): """Convert a model to a schema that uses camelCase field names.""" def _add_column_kwargs(self, kwargs, prop): """Update the provided kwargs based on the prop given. :param dict kwargs: A dictionary of kwargs to pass to the eventual field constructor. This argument is modified in place. :param prop: A column property used to determine how ``kwargs`` should be updated. :type prop: :class:`~sqlalchemy.orm.properties.ColumnProperty` """ super(CamelModelResourceConverter, self)._add_column_kwargs( kwargs, prop) kwargs["load_from"] = camelize(prop.key, uppercase_first_letter=False) kwargs["dump_to"] = camelize(prop.key, uppercase_first_letter=False)