예제 #1
0
    def _get_joinedload_object_for_splitted_include(
            cls, include: str, qs: QueryStringManager,
            permission_user: PermissionUser, current_schema: Schema, model):
        """
        Processes dot-splitted params from "include" and makes joinedload option for query.
        """
        joinedload_object = None
        for i, obj in enumerate(include.split(SPLIT_REL)):
            try:
                field = get_model_field(current_schema, obj)
            except Exception as e:
                raise InvalidInclude(str(e))

            if cls._is_access_foreign_key(obj, model,
                                          permission_user) is False:
                continue

            joinedload_object, current_schema = cls._get_or_update_joinedload_object(
                joinedload_object=joinedload_object,
                qs=qs,
                permission_user=permission_user,
                model=model,
                current_schema=current_schema,
                field=field,
                include=obj,
                path_index=i)
            try:
                model = cls._get_model(model, field)
            except ValueError as e:
                raise InvalidInclude(str(e))

        return joinedload_object
예제 #2
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 SPLIT_REL in include:
                current_schema = self.resource.schema
                for obj in include.split(SPLIT_REL):
                    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)
                    else:
                        joinload_object = joinload_object.joinedload(field)

                    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)

            query = query.options(joinload_object)

        return query
예제 #3
0
    def include(self):
        """Return fields to include

        :return list: a list of include information
        """
        include_param = self.qs.get('include', [])

        if current_app.config.get('MAX_INCLUDE_DEPTH') is not None:
            for include_path in include_param:
                if len(include_path.split(SPLIT_REL)) > current_app.config['MAX_INCLUDE_DEPTH']:
                    raise InvalidInclude("You can't use include through more than {} relationships"
                                         .format(current_app.config['MAX_INCLUDE_DEPTH']))

        return include_param.split(',') if include_param else []
예제 #4
0
    def _get_joinedload_object_for_include(cls, include, qs, permission_user,
                                           current_schema, model):
        """
        Processes params from "include" and makes joinedload option for query
        """
        try:
            field = get_model_field(current_schema, include)
        except Exception as e:
            raise InvalidInclude(str(e))

        joinedload_object, _ = cls._get_or_update_joinedload_object(
            joinedload_object=None,
            model=model,
            path_index=0,
            permission_user=permission_user,
            qs=qs,
            field=field,
            current_schema=current_schema,
            include=include)
        return joinedload_object
예제 #5
0
def compute_schema(schema_cls, default_kwargs, qs, include):
    """Compute a schema around compound documents and sparse fieldsets

    :param Schema schema_cls: the schema class
    :param dict default_kwargs: the schema default kwargs
    :param QueryStringManager qs: qs
    :param list include: the relation field to include data from

    :return Schema schema: the schema computed
    """
    # manage include_data parameter of the schema
    schema_kwargs = default_kwargs
    schema_kwargs['include_data'] = tuple()

    # collect sub-related_includes
    related_includes = {}

    if include:
        for include_path in include:
            field = include_path.split(SPLIT_REL)[0]

            if field not in schema_cls._declared_fields:
                raise InvalidInclude("{} has no attribute {}".format(
                    schema_cls.__name__, field))
            elif not isinstance(schema_cls._declared_fields[field],
                                Relationship):
                raise InvalidInclude(
                    "{} is not a relationship attribute of {}".format(
                        field, schema_cls.__name__))

            schema_kwargs['include_data'] += (field, )
            if field not in related_includes:
                related_includes[field] = []
            if SPLIT_REL in include_path:
                related_includes[field] += [
                    SPLIT_REL.join(include_path.split(SPLIT_REL)[1:])
                ]

    # make sure id field is in only parameter unless marshamllow will raise an Exception
    if schema_kwargs.get(
            'only') is not None and 'id' not in schema_kwargs['only']:
        schema_kwargs['only'] += ('id', )

    # create base schema instance
    schema = schema_cls(**schema_kwargs)

    # manage sparse fieldsets
    if schema.opts.type_ in qs.fields:
        tmp_only = set(schema.declared_fields.keys()) & set(
            qs.fields[schema.opts.type_])
        if schema.only:
            tmp_only &= set(schema.only)
        schema.only = tuple(tmp_only)

        # make sure again that id field is in only parameter unless marshamllow will raise an Exception
        if schema.only is not None and 'id' not in schema.only:
            schema.only += ('id', )

        schema.dump_fields = OrderedDict(
            **{
                name: val
                for name, val in schema.fields.items() if name in schema.only
            })

    # manage compound documents
    if include:
        for include_path in include:
            field = include_path.split(SPLIT_REL)[0]
            relation_field = schema.declared_fields[field]
            related_schema_cls = schema.declared_fields[field].__dict__[
                '_Relationship__schema']
            related_schema_kwargs = {}
            if 'context' in default_kwargs:
                related_schema_kwargs['context'] = default_kwargs['context']
            if isinstance(related_schema_cls, SchemaABC):
                related_schema_kwargs['many'] = related_schema_cls.many
                related_schema_cls = related_schema_cls.__class__
            if isinstance(related_schema_cls, str):
                related_schema_cls = class_registry.get_class(
                    related_schema_cls)
            related_schema = compute_schema(related_schema_cls,
                                            related_schema_kwargs, qs,
                                            related_includes[field] or None)
            relation_field.__dict__['_Relationship__schema'] = related_schema

    return schema