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
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
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
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
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
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
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
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
def load(self, query_builder: Query, relations: List[str]) -> Query: return query_builder.options( [orm.joinedload_all(relation) for relation in relations] )