Exemplo n.º 1
0
    def _set_relationship_load_option(
        query: Query,
        relationship: RelationshipProperty,
        load_option: RelationshipLoadOption,
    ) -> Query:
        """
        Sets a model's relationship loading technique for a select query. Eager loading
        will perform the necessary join at the time the select query is executed. Lazy
        loading will issue a distinct select statement for the relationship at the time
        when the property is first accessed.

        Arguments:
            query {Query} -- Base select query
            relationship {RelationshipProperty} -- Model's relationship property
            load_option {RelationshipLoadOption} -- Relationship loading technique

        Returns:
            Query -- Select query with relationship loading options
        """

        if load_option == RelationshipLoadOption.LAZY:
            query = query.options(lazyload(relationship))
        elif load_option == RelationshipLoadOption.EAGER:
            query = query.options(joinedload(relationship))

        return query
Exemplo n.º 2
0
    def _eagerload_includes(cls,
                            query: Query,
                            qs: QueryStringManager,
                            permission_user: PermissionUser = None,
                            self_json_api: SqlalchemyDataLayer = None):
        """
        Processes "include" param from querystring and applies permissions for included models.
        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
        :param PermissionUser permission_user: пермишены для пользователя
        :param self_json_api:
        :return Query: the query with includes eagerloaded
        """
        current_schema = self_json_api.resource.schema
        model = self_json_api.model
        for include in qs.include:
            if SPLIT_REL in include:
                joinedload_object = cls._get_joinedload_object_for_splitted_include(
                    include, qs, permission_user, current_schema, model)
            else:
                # Возможно пользовать неимеет доступа, к данному внешнему ключу
                if cls._is_access_foreign_key(include, model,
                                              permission_user) is False:
                    continue
                joinedload_object = cls._get_joinedload_object_for_include(
                    include, qs, permission_user, current_schema, model)
            query = query.options(joinedload_object)

        return query
Exemplo n.º 3
0
    def data_layer_get_object_update_query(self,
                                           *args,
                                           query: Query = None,
                                           qs: QueryStringManager = None,
                                           view_kwargs=None,
                                           self_json_api=None,
                                           **kwargs) -> Query:
        """
        Во время создания запроса к БД на выгрузку объекта. Тут можно пропатчить запрос к БД.
        Навешиваем ограничения на запрос, чтобы не тянулись поля из БД, которые данному
        пользователю не доступны. Также навешиваем фильтры, чтобы пользователь не смог увидеть
        записи, которые ему не доступны
        :param args:
        :param Query query: Сформированный запрос к БД
        :param QueryStringManager qs: список параметров для запроса
        :param view_kwargs: список фильтров для запроса
        :param self_json_api:
        :param kwargs:
        :return: возвращает пропатченный запрос к бд
        """
        permission: PermissionUser = self._get_permission_user(view_kwargs)
        permission_for_get: PermissionForGet = permission.permission_for_get(
            self_json_api.model)

        # Навешиваем фильтры (например пользователь не должен видеть некоторые поля)
        for i_join in permission_for_get.joins:
            query = query.join(*i_join)
        query = query.filter(*permission_for_get.filters)

        # Навешиваем ограничения по атрибутам (которые доступны & которые запросил пользователь)
        name_columns = permission_for_get.columns
        if qs:
            user_requested_columns = qs.fields.get(
                self_json_api.resource.schema.Meta.type_)
            if user_requested_columns:
                name_columns = list(
                    set(name_columns) & set(user_requested_columns))
        # Убираем relationship поля
        name_columns = [
            i_name for i_name in name_columns
            if i_name in self_json_api.model.__table__.columns.keys()
        ]
        required_columns_names = []
        for i_name in name_columns:
            required_columns_names.extend(
                get_required_fields(i_name, self_json_api.model))
        name_columns = list(set(name_columns) | set(required_columns_names))

        query = query.options(load_only(*name_columns))
        if qs:
            query = self._eagerload_includes(query,
                                             qs,
                                             permission,
                                             self_json_api=self_json_api)

        # Запретим использовать стандартную функцию eagerload_includes для присоединения сторонних молелей
        self_json_api.eagerload_includes = lambda x, y: x
        return query
Exemplo n.º 4
0
    def data_layer_get_collection_update_query(self,
                                               *args,
                                               query: Query = None,
                                               qs: QueryStringManager = None,
                                               view_kwargs=None,
                                               self_json_api=None,
                                               **kwargs) -> Query:
        """
        Во время создания запроса к БД на выгрузку объектов. Тут можно пропатчить запрос к БД
        :param args:
        :param Query query: Сформированный запрос к БД
        :param QueryStringManager qs: список параметров для запроса
        :param view_kwargs: список фильтров для запроса
        :param self_json_api:
        :param kwargs:
        :return: возвращает пропатченный запрос к бд
        """
        permission: PermissionUser = self._get_permission_user(view_kwargs)
        permission_for_get: PermissionForGet = permission.permission_for_get(
            self_json_api.model)

        # Навешиваем фильтры (например пользователь не должен видеть некоторые поля)
        for i_join in permission_for_get.joins:
            query = query.join(*i_join)
        query = query.filter(*permission_for_get.filters)

        # Навешиваем ограничения по атрибутам (которые доступны & которые запросил пользователь)
        name_columns = permission_for_get.columns
        user_requested_columns = qs.fields.get(
            self_json_api.resource.schema.Meta.type_)
        if user_requested_columns:
            name_columns = list(
                set(name_columns) & set(user_requested_columns))

        # required fields (from Meta.required_fields)
        required_columns_names = []
        for i_name in name_columns:
            required_columns_names.extend(
                get_required_fields(i_name, self_json_api.model))

        # remove relationship fields
        name_columns = list(
            set(name_columns)
            & set(get_columns_for_query(self_json_api.model)))
        name_columns = list(set(name_columns) | set(required_columns_names))

        query = query.options(load_only(*name_columns))

        # Запретим использовать стандартную функцию eagerload_includes для присоединения сторонних молелей
        setattr(self_json_api, "eagerload_includes", False)
        query = self._eagerload_includes(query,
                                         qs,
                                         permission,
                                         self_json_api=self_json_api)
        return query
Exemplo n.º 5
0
 def get_collection_contents_qry(self, parent_id, limit=None, offset=None):
     """Find first level of collection contents by containing collection parent_id"""
     DCE = model.DatasetCollectionElement
     qry = Query(DCE).filter(DCE.dataset_collection_id == parent_id)
     qry = qry.order_by(DCE.element_index)
     qry = qry.options(joinedload('child_collection'), joinedload('hda'))
     if limit is not None:
         qry = qry.limit(int(limit))
     if offset is not None:
         qry = qry.offset(int(offset))
     return qry
Exemplo n.º 6
0
    def _add_join(self, query: Query) -> Query:
        for key in self._foreings:
            planet_table = PLANET_TABLES[key]

            resonance_attr = getattr(self.resonance_cls, "%s_id" % key)
            query = query.outerjoin(planet_table, resonance_attr == planet_table.id)
            if self._load_related:
                options = contains_eager(getattr(self.resonance_cls, key), alias=planet_table)
                query = query.options(options)

        return query
Exemplo n.º 7
0
    def _add_join(self, query: Query) -> Query:
        for key in self._foreings:
            planet_table = PLANET_TABLES[key]

            resonance_attr = getattr(self.resonance_cls, '%s_id' % key)
            query = query.outerjoin(planet_table,
                                    resonance_attr == planet_table.id)
            if self._load_related:
                options = contains_eager(getattr(self.resonance_cls, key),
                                         alias=planet_table)
                query = query.options(options)

        return query
Exemplo n.º 8
0
    def _alter_query__add_nested_nplus1loader(self, query: Query,
                                              mapper: Mapper, attr_name: str,
                                              is_relationship: bool):
        """ When loading a nested relationship, apply another nplus1loader to it """
        # Only apply to relationships
        if is_relationship:
            Model = self.parent.class_
            relationship = getattr(Model, attr_name)
            related_Model = relationship.property.mapper.class_

            return query.options(
                defaultload(relationship).default_columns(
                    related_Model).nplus1loader('*'), )
        # No special options for columns
        else:
            return query
Exemplo n.º 9
0
 def load(self, query_builder: Query, relations: List[str]) -> Query:
     return query_builder.options(
         [orm.joinedload_all(relation) for relation in relations]
     )