Exemplo n.º 1
0
    def sort_query(self, query, sort_info):
        """Sort query according to jsonapi 1.0

        :param Query query: sqlalchemy query to sort
        :param list sort_info: sort information
        :return Query: the sorted query
        """
        for sort_opt in sort_info:
            field = sort_opt['field']
            if sort_opt.get('relationship'):
                current_schema = self.resource.schema
                for obj in sort_opt['relationship'].split('.'):
                    current_schema = get_related_schema(current_schema, obj)

                    if not hasattr(current_schema.Meta, 'model'):
                        raise InvalidSort("In order to sort on a relationship, meta model must be defined")

                    relationship_model = current_schema.Meta.model
                    query = query.join(relationship_model)


                relationship_model = current_schema.Meta.model
                model_field = get_model_field(current_schema, sort_opt['field'])
                query = query.order_by(getattr(getattr(relationship_model, model_field), sort_opt['order'])())
            elif not hasattr(self.model, field):
                raise InvalidSort("{} has no attribute {}".format(self.model.__name__, field))
            else:
                query = query.order_by(getattr(getattr(self.model, field), sort_opt['order'])())
        return query
Exemplo n.º 2
0
    def sorting(self):
        """Return fields to sort by including sort name for SQLAlchemy and row
        sort parameter for other ORMs

        :return list: a list of sorting information

        Example of return value::

            [
                {'field': 'created_at', 'order': 'desc'},
            ]

        """
        if self.qs.get('sort'):
            sorting_results = []
            for sort_field in self.qs['sort'].split(','):
                field = sort_field.replace('-', '')
                if field not in self.schema._declared_fields:
                    raise InvalidSort("{} has no attribute {}".format(
                        self.schema.__name__, field))
                if field in get_relationships(self.schema):
                    raise InvalidSort(
                        "You can't sort on {} because it is a relationship field"
                        .format(field))
                field = get_model_field(self.schema, field)
                order = 'desc' if sort_field.startswith('-') else 'asc'
                sorting_results.append({'field': field, 'order': order})
            return sorting_results

        return []
Exemplo n.º 3
0
    def sorting(self):
        """Return fields to sort by including sort name for SQLAlchemy and row
        sort parameter for other ORMs

        :return list: a list of sorting information

        Example of return value::

            [
                {'field': 'created_at', 'order': 'desc'},
            ]

        """
        if self.qs.get('sort'):
            sorting_results = []
            for sort_field in self.qs['sort'].split(','):
                field = sort_field.replace('-', '')
                order = 'desc' if sort_field.startswith('-') else 'asc'
                if '.' in field:
                    # attempting to sort on relationship
                    relationship_path = '.'.join(field.split('.')[0:-1])
                    target_attribute = field.split('.')[-1]

                    relationship_schema = get_related_schema_path(
                        self.schema, relationship_path)
                    if not get_model_field(relationship_schema,
                                           target_attribute):
                        raise InvalidSort("Invalid sort path {}".format(field))

                    sorting_results.append({
                        'field': target_attribute,
                        'order': order,
                        'relationship': relationship_path
                    })
                elif field not in self.schema._declared_fields:
                    raise InvalidSort("{} has no attribute {}".format(
                        self.schema.__name__, field))
                elif field in get_relationships(self.schema):
                    raise InvalidSort(
                        "You can't sort on {} because it is a relationship field"
                        .format(field))
                else:
                    field = get_model_field(self.schema, field)
                    sorting_results.append({'field': field, 'order': order})
            return sorting_results

        return []
Exemplo n.º 4
0
    def eagerload_includes(self, query, qs):
        """Use eagerload feature of sqlalchemy to optimize data retrieval for include querystring parameter

        :param Query query: sqlalchemy queryset
        :param QueryStringManager qs: a querystring manager to retrieve information from url
        :return Query: the query with includes eagerloaded
        """
        for include in qs.include:
            joinload_object = None

            if '.' in include:
                current_schema = self.resource.schema
                for obj in include.split('.'):
                    try:
                        field = get_model_field(current_schema, obj)
                    except Exception as e:
                        raise InvalidInclude(str(e))

                    if joinload_object is None:
                        joinload_object = joinedload(field, innerjoin=True)
                    else:
                        joinload_object = joinload_object.joinedload(
                            field, innerjoin=True)

                    related_schema_cls = get_related_schema(
                        current_schema, obj)

                    if isinstance(related_schema_cls, SchemaABC):
                        related_schema_cls = related_schema_cls.__class__
                    else:
                        related_schema_cls = class_registry.get_class(
                            related_schema_cls)

                    current_schema = related_schema_cls
            else:
                try:
                    field = get_model_field(self.resource.schema, include)
                except Exception as e:
                    raise InvalidInclude(str(e))

                joinload_object = joinedload(field, innerjoin=True)

            query = query.options(joinload_object)

        return query
Exemplo n.º 5
0
    def _get_relationship_data(self):
        """Get useful data for relationship management"""
        relationship_field = request.path.split('/')[-1]

        if relationship_field not in get_relationships(self.schema):
            raise RelationNotFound("{} has no attribute {}".format(self.schema.__name__, relationship_field))

        related_type_ = self.schema._declared_fields[relationship_field].type_
        related_id_field = self.schema._declared_fields[relationship_field].id_field
        model_relationship_field = get_model_field(self.schema, relationship_field)

        return relationship_field, model_relationship_field, related_type_, related_id_field
Exemplo n.º 6
0
    def column(self):
        """Get the column object

        :param DeclarativeMeta model: the model
        :param str field: the field
        :return InstrumentedAttribute: the column to filter on
        """
        field = self.name

        model_field = get_model_field(self.schema, field)

        try:
            attr = getattr(self.model, model_field)
        except AttributeError:
            raise InvalidFilters("{} has no attribute {}".format(
                self.model.__name__, model_field))

        name = self.filter_.get('name')
        if '[' in name:
            if not isinstance(attr.type, Indexable):
                raise InvalidFilters(
                    "{} is not an indexable column".format(model_field))
            name = name[name.index('['):]
            while name.startswith('['):
                item, name = name[1:].split(']', 1)
                try:
                    item = json.loads(item)
                except json.decoder.JSONDecodeError:
                    raise InvalidFilters(
                        "{} is not a valid index".format(item))
                try:
                    attr = attr[item]
                except Exception:
                    raise InvalidFilters(
                        "{} is not a valid indexing expression".format(
                            self.filter_.get('name')))
            if name == ".as_string()":
                attr = attr.as_string()
            elif name == ".as_integer()":
                attr = attr.as_integer()
            elif name == ".as_float()":
                attr = attr.as_float()
            elif name == ".as_boolean()":
                attr = attr.as_boolean()
            elif len(name) > 0:
                raise InvalidFilters(
                    "{} is not a valid indexing expression".format(
                        self.filter_.get('name')))

        return attr
Exemplo n.º 7
0
    def related_model(self):
        """Get the related model of a relationship field

        :return DeclarativeMeta: the related model
        """
        relationship_field = self.name

        if relationship_field not in get_relationships(self.schema):
            raise InvalidFilters("{} has no relationship attribute {}".format(
                self.schema.__name__, relationship_field))

        return getattr(self.model,
                       get_model_field(
                           self.schema,
                           relationship_field)).property.mapper.class_
    def column(self):
        """Get the column object

        :param DeclarativeMeta model: the model
        :param str field: the field
        :return InstrumentedAttribute: the column to filter on
        """
        field = self.name

        model_field = get_model_field(self.schema, field)

        try:
            return getattr(self.model, model_field)
        except AttributeError:
            raise InvalidFilters("{} has no attribute {}".format(
                self.model.__name__, model_field))
Exemplo n.º 9
0
    def related_model(self):
        """Get the related model of a related (relationship or nested) field

        :return DeclarativeMeta: the related model
        """
        related_field_name = self.name

        related_fields = get_relationships(self.schema) + get_nested_fields(
            self.schema)
        if related_field_name not in related_fields:
            raise InvalidFilters(
                "{} has no relationship or nested attribute {}".format(
                    self.schema.__name__, related_field_name))

        return getattr(self.model,
                       get_model_field(
                           self.schema,
                           related_field_name)).property.mapper.class_