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
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
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 []
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
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