Ejemplo n.º 1
0
def client_func(genericAPIView, user, app, model, func_name, params):
    """云函数, 由客户端直接调用的服务函数
        """
    func, options = find_func(app, model, func_name)
    if not func:
        raise exceptions.BusinessException(
            error_code=exceptions.FUNCTION_NOT_FOUNT,
            error_data=f'no such func: {func_name} found',
        )

    if options.get('login_required', False):
        if not user.is_authenticated:
            raise PermissionDenied()

    view_context = {'view': genericAPIView}
    params['view_context'] = view_context

    result = func(user, **params)
    # TODO:考虑函数的返回结果类型。1. 实体,2.实体列表,3.字典,4.无返回,针对不同的结果给客户端反馈
    if isinstance(result, requests.Response):
        return HttpResponse(result, result.headers.get('Content-Type', None))
    if isinstance(result, (list, dict)):
        return success_response(result)
    if isinstance(result, genericAPIView.model):
        serializer = genericAPIView.get_serializer(result)
        return success_response(serializer.data)
    return success_response()
Ejemplo n.º 2
0
 def handle(self):
     request = self.context['request']
     try:
         return self.bsm_batch_action(request, self.bsm_batch_queryset,
                                      self.data.get('payload', {}))
     except Exception as e:
         raise exceptions.BusinessException(
             error_code=exceptions.BATCH_ACTION_HAND_ERROR,
             error_data=str(e))
Ejemplo n.º 3
0
    def validate_call_api_permission(self, request):
        """校验是否具有调用指定接口的权限

        例如管理人员调用商品的列表接口,如果开启了使用接口校验,则需要校验此

        用户是否具有调用此接口的权限,如果没有对应的权限,则就抛出没有权限

        注意:如果是云函数,则另做处理
        """
        if not settings.MANAGE_API_PERMISSION_VALIDATE_ENABLE:
            return

        # if self.action == 'func':
        #     func_name = request.data.get('func_name', None) or request.GET.get(
        #         'func_name', None
        #     )
        #     perm_list = [f'basebone_api_model_func_{func_name}']
        # else:
        #     perm_map = {
        #         'create': ['create'],
        #         'list': ['list'],
        #         'set': ['set'],
        #         'destroy': ['retrieve', 'destroy'],
        #         'update': ['retrieve', 'update'],
        #         'partial_update': ['retrieve', 'partial_update'],
        #         'custom_patch': ['retrieve', 'custom_patch'],
        #         'update_sort': ['retrieve', 'update_sort'],
        #         'batch': ['retrieve', 'batch'],
        #         'export_file': ['export_file'],
        #     }

        #     perm_list = [
        #         f'basebone_api_model_{item}' for item in perm_map.get(self.action)
        #     ]
        # check = request.user.has_perms(perm_list)
        # if not check:
        #     raise exceptions.BusinessException(error_code=exceptions.REQUEST_FORBIDDEN)

        # TODO: 暂时提供测试而已,后面会改掉
        perm_map = {
            'retrieve': ['view'],
            'create': ['add'],
            'list': ['view'],
            'update': ['view', 'change'],
            'partial_update': ['view', 'change'],
            'destroy': ['view', 'delete'],
        }
        perm_list = [
            f'{item}_{self.model_slug}'
            for item in perm_map.get(self.action, [])
        ]

        if perm_list:
            check = request.user.has_perms(perm_list)
            if not check:
                raise exceptions.BusinessException(
                    error_code=exceptions.REQUEST_FORBIDDEN)
Ejemplo n.º 4
0
    def perform_authentication(self, request):
        """
        截断,校验对应的 app 和 model 是否合法以及赋予当前对象对应的属性值

        - 检验 app 和 model 是否合法
        - 加载 admin 模块
        - 记录模型对象
        - 处理展开字段
        - 处理树形数据
        - 给数据自动插入用户数据
        """
        result = super().perform_authentication(request)
        self.app_label, self.model_slug = self.kwargs.get(
            'app'), self.kwargs.get('model')

        # 检测模型是否合法
        try:
            self.model = apps.get_model(self.app_label, self.model_slug)
        except LookupError:
            raise exceptions.BusinessException(
                error_code=exceptions.MODEL_SLUG_IS_INVALID)

        # 检测方法是否允许访问
        model_str = f'{self.app_label}__{self.model_slug}'
        expose = exposed_apis.get(model_str, None)
        real_action = self.action
        if self.action == 'set':
            real_action = 'list'
        if (not expose or not expose.get('actions', None)
                or real_action not in expose['actions']):
            raise exceptions.BusinessException(
                error_code=exceptions.THIS_ACTION_IS_NOT_AUTHENTICATE)

        meta.load_custom_admin_module()
        self.get_expand_fields()
        self._get_data_with_tree(request)

        return result
Ejemplo n.º 5
0
    def validate_data(self, value):
        """
        - 校验 data
        - 记录当前批量的数据
        """
        model = self.context.get('view').model
        filter_params = {f'{model._meta.pk.name}__in': value}

        queryset = model.objects.filter(**filter_params)
        if len(value) != queryset.count():
            raise exceptions.BusinessException(
                error_code=exceptions.PARAMETER_BUSINESS_ERROR,
                error_message='列表中包含不合法 id 的数据')
        self.bsm_batch_queryset = queryset
        return value
Ejemplo n.º 6
0
    def validate_action(self, value):
        """
        - 校验 action
        - 记录当前批量动作
        """
        view = self.context['view']
        actions = get_model_batch_actions(view.model, end=view.end_slug)
        for key, val in common_action_map.items():
            actions.setdefault(key, val)

        if value not in actions:
            raise exceptions.BusinessException(
                error_code=exceptions.PARAMETER_FORMAT_ERROR,
                error_data='传入的 action: {} 不支持'.format(value))
        self.bsm_batch_action = actions[value]
        return value
Ejemplo n.º 7
0
def manage_func(genericAPIView, user, app, model, func_name, params):
    """云函数, 由客户端直接调用的服务函数
        """
    # import ipdb; ipdb.set_trace()
    func, options = find_func(app, model, func_name)
    if not func:
        raise exceptions.BusinessException(
            error_code=exceptions.FUNCTION_NOT_FOUNT,
            error_data=f'no such func: {func_name} found',
        )
    if options.get('login_required', False):
        if not user.is_authenticated:
            raise PermissionDenied()
    if options.get('staff_required', False):
        if not user.is_staff:
            raise PermissionDenied()
    if options.get('superuser_required', False):
        if not user.is_superuser:
            raise PermissionDenied()

    view_context = {'view': genericAPIView}
    params['view_context'] = view_context
    result = func(user, **params)

    # TODO:考虑函数的返回结果类型。1. 实体,2.实体列表,3.字典,4.无返回,针对不同的结果给客户端反馈
    if isinstance(result, requests.Response):
        response = HttpResponse(result, result.headers.get('Content-Type', None))
        if 'Content-disposition' in result.headers:
            response['Content-disposition'] = result.headers.get('Content-disposition')
        return response
    if (
        isinstance(result, list)
        or isinstance(result, dict)
        or isinstance(result, str)
        or isinstance(result, bytes)
    ):
        return success_response(result)
    return success_response()
Ejemplo n.º 8
0
    def export_file(self, request, *args, **kwargs):
        """输出 excel 和 excel 文件

        ```
        Params:
            fileformat string csv | excel 形式使用 querystring 的形式
        ```
        """
        # 检测是否支持导出
        admin_class = self.get_bsm_model_admin()
        if self._export_type_config.get('version') != 'v2':
            if admin_class:
                exportable = getattr(admin_class, admin.BSM_EXPORTABLE, False)
                if not exportable:
                    raise exceptions.BusinessException(
                        error_code=exceptions.MODEL_EXPORT_IS_NOT_SUPPORT)
            else:
                raise exceptions.BusinessException(
                    error_code=exceptions.MODEL_EXPORT_IS_NOT_SUPPORT)

        if not self._export_type_config:
            raise exceptions.BusinessException(
                error_code=exceptions.MODEL_EXPORT_IS_NOT_SUPPORT)

        csv_file, excel_file = 'csv', 'excel'
        valid_list = (csv_file, excel_file)

        file_type = self._export_type_config['file_type']
        file_type = file_type if file_type in valid_list else csv_file
        queryset = self.filter_queryset(self.get_queryset())

        export_version = self._export_type_config.get('version')
        if export_version == 'v2':
            serializer_class = get_export_serializer_class(
                self.model,
                self.get_serializer_class(),
                version=export_version)

            return renderers_v2.csv_render(
                self.model,
                queryset,
                serializer_class,
                export_config=self._export_type_config,
            )

        serializer_queryset_handler = self._export_type_config.get(
            'serializer_queryset_handler')
        if serializer_queryset_handler:
            func_handler = getattr(admin_class, serializer_queryset_handler,
                                   None)
            if func_handler:
                queryset = func_handler(queryset)

        actual_app_label = self._export_type_config.get('actual_app_label')
        actual_model_slug = self._export_type_config.get('actual_model_slug')

        if actual_app_label and actual_model_slug:
            self.model = apps.get_model(actual_app_label, actual_model_slug)

        custom_serializer_class = self._export_type_config.get(
            'serializer_class')
        serializer_class = get_export_serializer_class(
            self.model,
            self.get_serializer_class(),
            custom_serializer_class=custom_serializer_class,
        )
        return renderers.csv_render(self.model,
                                    queryset,
                                    serializer_class,
                                    export_config=self._export_type_config)
Ejemplo n.º 9
0
    def check_app_model(self, request):
        # 是否对结果集进行去重
        self.basebone_distinct_queryset = False

        # 模型角色配置
        self.model_role_config = None

        self.app_label, self.model_slug = self.kwargs.get(
            'app'), self.kwargs.get('model')

        try:
            self.model = apps.get_model(self.app_label, self.model_slug)
        except LookupError:
            raise exceptions.BusinessException(
                error_code=exceptions.MODEL_SLUG_IS_INVALID)

        # 加载 admin 配置
        meta.load_custom_admin_module()

        if self.action == 'export_file':
            admin_class = self.get_bsm_model_admin()
            self._export_type_config = None
            if admin_class:
                exportable = getattr(admin_class, admin.BSM_EXPORTABLE, False)
                if exportable:
                    exportable_map = {item['key']: item for item in exportable}
                    file_type = self.request.query_params.get('fileformat')
                    if file_type in exportable_map:
                        valid_item = exportable_map[file_type]
                    else:
                        valid_item = [
                            item for item in exportable if item['default']
                        ][0]

                    self._export_type_config = valid_item

                    if request.method.lower() == 'get':
                        if 'basebone_export_config' in list(
                                dict(request.query_params)):
                            try:
                                basebone_export_config = json.loads(
                                    request.query_params.get(
                                        'basebone_export_config'))
                            except Exception:
                                basebone_export_config = None
                            self._export_type_config = basebone_export_config
                    elif request.method.lower() == 'post':
                        if 'basebone_export_config' in request.data:
                            basebone_export_config = request.data.get(
                                'basebone_export_config')
                            if not isinstance(basebone_export_config, dict):
                                basebone_export_config = None
                            self._export_type_config = basebone_export_config

                    if not self._export_type_config:
                        raise exceptions.BusinessException(
                            error_code=exceptions.MODEL_EXPORT_IS_NOT_SUPPORT)

                    # FIXME: 如果不是新版导出,则重置 app_label 和 model_slug
                    if self._export_type_config.get('version') != 'v2':
                        self.app_label = self._export_type_config['app_label']
                        self.model_slug = self._export_type_config[
                            'model_slug']

                        # 检测模型是否合法
                        try:
                            self.model = apps.get_model(
                                self.app_label, self.model_slug)
                        except LookupError:
                            raise exceptions.BusinessException(
                                error_code=exceptions.MODEL_SLUG_IS_INVALID)
                else:
                    if request.method.lower() == 'get':
                        if 'basebone_export_config' in list(
                                dict(request.query_params)):
                            try:
                                basebone_export_config = json.loads(
                                    request.query_params.get(
                                        'basebone_export_config'))
                            except Exception:
                                basebone_export_config = None
                            self._export_type_config = basebone_export_config
                    elif request.method.lower() == 'post':
                        if 'basebone_export_config' in request.data:
                            basebone_export_config = request.data.get(
                                'basebone_export_config')
                            if not isinstance(basebone_export_config, dict):
                                basebone_export_config = None
                            self._export_type_config = basebone_export_config
                    if not self._export_type_config:
                        raise exceptions.BusinessException(
                            error_code=exceptions.MODEL_EXPORT_IS_NOT_SUPPORT)
            else:
                if request.method.lower() == 'get':
                    if 'basebone_export_config' in list(
                            dict(request.query_params)):
                        try:
                            basebone_export_config = json.loads(
                                request.query_params.get(
                                    'basebone_export_config'))
                        except Exception:
                            basebone_export_config = None
                        self._export_type_config = basebone_export_config
                elif request.method.lower() == 'post':
                    if 'basebone_export_config' in request.data:
                        basebone_export_config = request.data.get(
                            'basebone_export_config')
                        if not isinstance(basebone_export_config, dict):
                            basebone_export_config = None
                        self._export_type_config = basebone_export_config
                if not self._export_type_config:
                    raise exceptions.BusinessException(
                        error_code=exceptions.MODEL_EXPORT_IS_NOT_SUPPORT)
Ejemplo n.º 10
0
def reverse_many_to_many(instance, field, data):
    """
    处理反向多对多关系的数据

    Params:
        instance object 对象
        field object 反向的字段
        data list 反向的模型字典列表数据
    """

    # 反向关系的数据必须是数组
    if not isinstance(data, list):
        raise exceptions.BusinessException(
            error_code=exceptions.PARAMETER_FORMAT_ERROR,
            error_data=f'{field.name}: {data} 只能是列表',
        )

    # TODO: 如果使用了自定义的中间表,此种业务暂时不做处理
    # TODO: 支持自定义中间表,如果非单纯中间表让它自行报异常,中间表必填值以后可以考虑使用 through_defaults
    # if field.through_fields:
    #     return

    model = field.related_model
    related_name = meta.get_accessor_name(field)
    reverse_manager = getattr(instance, related_name, None) if related_name else None

    if not data:
        # 传入数据为空的场景
        # 更新操作,如果传入空的数据,则清除掉此对象所有的数据
        if reverse_manager:
            reverse_manager.clear()
        return

    # 传入数据不为空的情况下
    pk_field_name = model._meta.pk.name
    # 迭代处理反向数据,这个时候还没有处理数据和对象的关系
    reverse_object_list = set()
    for item_value in data:
        if  isinstance(item_value, dict):
            if  pk_field_name not in item_value:
                # 创建反向模型的数据
                serializer = create_serializer_class(model)(data=item_value)
                serializer.is_valid(raise_exception=True)
                item_instance = serializer.save()
            else:
                # 更新反向模型的数据
                item_instance = model.objects.filter(
                    **{pk_field_name: item_value[pk_field_name]}
                ).first()
                if not item_instance:
                    raise exceptions.BusinessException(
                        error_code=exceptions.OBJECT_NOT_FOUND,
                        error_data=f'{pk_field_name}: {item_value} 指定的主键找不到对应的数据',
                    )
                serializer = create_serializer_class(model)(
                    instance=item_instance, data=item_value, partial=True
                )
                serializer.is_valid(raise_exception=True)
                serializer.save()
            reverse_object_list.add(item_instance.pk)
        else:
            reverse_object_list.add(item_value)

    # 处理数据和 instance 之间的关系
    if reverse_manager:
        reverse_manager.set(list(reverse_object_list))