def _get_menu_from_autobuild(self): """根据模型自定义菜单""" export_apps = get_export_apps() if not export_apps: return success_response([]) try: result, id_index = [], 0 for app_name in export_apps: application = apps.get_app_config(app_name) for model_item in application.get_models(): id_index += 1 result.append({ "id": id_index, "name": model_item._meta.verbose_name, "icon": None, "parent": None, "page": "list", "permission": None, "model": f"{app_name}__{model_item._meta.model_name}", "sequence": 0, "menu": [] }) return success_response(result) except Exception: return success_response([])
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()
def client_update(genericAPIView, request, partial, set_data): """全量更新数据""" with transaction.atomic(): client_user_pip.add_login_user_data(genericAPIView, set_data) forward_relation_hand(genericAPIView.model, set_data) # partial = kwargs.pop('partial', False) instance = genericAPIView.get_object() old_instance = copy(instance) serializer = genericAPIView.get_validate_form(genericAPIView.action)( instance, data=set_data, partial=partial) serializer.is_valid(raise_exception=True) instance = genericAPIView.perform_update(serializer) reverse_relation_hand(genericAPIView.model, set_data, instance) instance = genericAPIView.get_queryset().get(pk=instance.pk) # with transaction.atomic(): log.debug( 'sending Post Update signal with: model: %s, instance: %s', genericAPIView.model, instance, ) post_bsm_create.send( sender=genericAPIView.model, instance=instance, create=False, request=genericAPIView.request, old_instance=old_instance, ) serializer = genericAPIView.get_serializer( genericAPIView.get_queryset().get(pk=instance.pk)) return success_response(serializer.data)
def _get_menu_from_database(self): """从数据库中获取菜单""" user = self.request.user # permissions = self.request.user.get_all_permissions() # permission_filter = (Q(permission=None) | Q(permission='') | Q(permission__in=permissions)) menus = Menu.objects.prefetch_related('parent').order_by('sequence','id') if user.is_superuser else \ Menu.objects.filter(Q(groups__in=self.request.user.groups.all()) | Q(groups__isnull=True)).prefetch_related('parent').order_by('sequence','id') fields = {field.name for field in Menu._meta.fields } - {'id', 'parent', 'permission', 'name'} menus_map = { menu.id: dict({field: getattr(menu, field) for field in fields}, **{ 'name': menu.display_name, 'parent_id': menu.parent_id, 'children': [] }) for menu in menus } for _, menu in menus_map.items(): parent_id = menu['parent_id'] if parent_id and parent_id in menus_map: menus_map[parent_id]['children'].append(menu) menus_data = [ m for _, m in menus_map.items() if not m.get('parent_id') ] return success_response(menus_data)
def update_sort(genericAPIView, request, data): if data.get('dragId') == data.get('hoverId'): return instance = genericAPIView.model admin = genericAPIView.get_bsm_model_admin() sort_key = admin.sort_key from django.db.models import F, Q with transaction.atomic(): dragItem = instance.objects.filter(id=data['dragId']).first() hoverItem = instance.objects.filter(id=data['hoverId']).first() dragIndex = getattr(dragItem, sort_key) hoveIndex = getattr(hoverItem, sort_key) isDownward = dragIndex < hoveIndex or ( dragIndex == hoveIndex and dragItem.id < hoverItem.id ) instance.objects.filter(id=data['dragId']).update( **{'parent': hoverItem.parent, sort_key: hoveIndex + 1} ) instance.objects.filter( Q(parent=hoverItem.parent), ~Q(id=dragItem.id), Q(**{f'{sort_key}__gt': hoveIndex}), ).update(**{f'{sort_key}': F(f'{sort_key}') + 2}) up = Q(id__gt=hoverItem.id) if isDownward else Q(id__gte=hoverItem.id) instance.objects.filter( Q(parent=hoverItem.parent), ~Q(id=dragItem.id), Q(**{f'{sort_key}': hoveIndex}), up, ).update(**{f'{sort_key}': F(f'{sort_key}') + 2}) return success_response(instance.objects.all().values())
def delete_by_conditon(genericAPIView): """按查询条件删除""" queryset = genericAPIView.filter_queryset(genericAPIView.get_queryset()) deleted, rows_count = queryset.delete() result = {'deleted': deleted} return success_response(result)
def token(self, request, *args, **kwargs): """ ## 生成对应的上传签名 **参数放在 query string 中,形式如 ?service=aliyun** 当前的服务只支持 (aliyun, 阿里云) ### Returns #### 阿里云的返回数据结构如下: ``` { "error_code": "0", "error_message": "", "result": { "accessid": "LTAIudMj4IZMpCCn", "host": "speedapi.oss-cn-shanghai.aliyuncs.com", "policy": "eyJleHBpcmF0aW9uIjogIjIwMTgV5IiwgIm1lZGlhLyJdXX0=", "signature": "9aOOMFzwwVQl0u/sFgdLKRSyeIw=", "expire": 1539770693, "dir": "media/" } } ``` #### 腾讯云 COS 返回的数据结构如下 { 'startTime': 1592561936 'expiredTime': 1592561966, 'expiration': '2020-06-19T10:19:26Z', 'requestId': '4332ced3-50a7-48fb-a35a-cb9efcec95d9', 'bucket': 'test-20188932', 'region': 'ap-guangzhou', 'credentials': { 'sessionToken': 'kg1Mg_UmDtAJ3wQA', 'tmpSecretId': 'AKIDy_RmF9qEg1geYsrJ_UwR4WWYcDGM2iy71R', 'tmpSecretKey': 'Q5FPMVsD=' }, } """ service = request.query_params.get('service', site_setting['upload_provider']) result = {'provider': None} if service in ['aliyun', 'oss']: result = aliyun.get_token() result['provider'] = 'oss' elif service in ['tencent', 'cos']: result = tencent.post_object_token() result['provider'] = 'cos' elif service == 'file_storage': result = { 'provider': 'file_storage', 'policy': '', 'dir': '', 'host': '/basebone/storage/upload' } return success_response(result)
def destroy(genericAPIView, request, scope=''): """删除数据""" instance = genericAPIView.get_object() old_instance = copy(instance) genericAPIView.perform_destroy(instance) post_bsm_delete.send( sender=genericAPIView.model, instance=old_instance, request=genericAPIView.request, scope=scope ) return success_response()
def get_userinfo(self, request, *args, **kwargs): """ ## 检测是否是否登录 如果用户已登录,则直接返回此登录用户的数据结构 """ serializer = self.get_serializer(request.user) result = {} result.update(serializer.data) result['permissions'] = request.user.get_all_permissions() return success_response(result)
def _get_menu_from_custom(self): """从自定义的菜单配置中获取菜单""" menu_module = module.get_bsm_global_module( module.BSM_GLOBAL_MODULE_MENU) result = getattr(menu_module, module.BSM_GLOBAL_MODULE_MENU_MANAGE, None) by_role = getattr(settings, 'BSM_MANAGE_MENU_BY_ROLE', False) if not by_role: return success_response(result['default']) else: groups = {item.name for item in self.request.user.groups.all()} if not groups: return success_response([]) for item in groups: if item in result: return success_response(result[item]) return success_response([])
def manage_create(genericAPIView, request, set_data): """ 这里校验表单和序列化类分开创建 原因:序列化类有可能嵌套 """ many = isinstance(set_data, list) with transaction.atomic(): forward_relation_hand(genericAPIView.model, set_data) serializer = genericAPIView.get_validate_form(genericAPIView.action)( data=set_data, context=genericAPIView.get_serializer_context(), many=many) serializer.is_valid(raise_exception=True) instance = genericAPIView.perform_create(serializer) if many: # 如果是批量插入,则直接返回 return success_response() # 如果有联合查询,单个对象创建后并没有联合查询 instance = genericAPIView.get_queryset().filter(pk=instance.pk).first() serializer = genericAPIView.get_serializer(instance) reverse_relation_hand(genericAPIView.model, set_data, instance, detail=False) log.debug( 'sending Post Save signal with: model: %s, instance: %s', genericAPIView.model, instance, ) post_bsm_create.send(sender=genericAPIView.model, instance=instance, create=True, request=genericAPIView.request, old_instance=None, scope='admin') return success_response(serializer.data)
def batch(self, request, app, model, **kwargs): """ ## 批量操作 ```python {action: 动作, data: 主键的列表} ``` """ serializer = batch_actions.BatchActionForm( data=request.data, context=self.get_serializer_context()) serializer.is_valid(raise_exception=True) serializer.handle() return success_response()
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()
def display(genericAPIView, display_fields): """查询操作,取名display,避免跟列表list冲突""" queryset = genericAPIView.filter_queryset(genericAPIView.get_queryset()) page = genericAPIView.paginate_queryset(queryset) if page is not None: """分页查询""" serializer = genericAPIView.get_serializer(page, many=True) result = filter_display_fields(serializer.data, display_fields) response = genericAPIView.get_paginated_response(result) result = response.data else: serializer = genericAPIView.get_serializer(queryset, many=True) result = filter_display_fields(serializer.data, display_fields) return success_response(result)
def get_all(self, request, *args, **kargs): """获取所有的客户端配置,包括schema, admin """ self._load_bsm_admin_module() data = { 'schemas': get_app_field_schema(), 'admins': get_app_admin_config() } json_object_schemas, json_array_item_schemas = get_app_json_field_schema( ) json_admin_configs = get_json_field_admin_config( json_object_schemas, json_array_item_schemas) data['schemas'].update(json_object_schemas) data['schemas'].update(json_array_item_schemas) data['admins'].update(json_admin_configs) return success_response(data)
def upload(request): key, policy, file = request.data['key'], request.data['policy'], request.data['file'] storage_path = site_setting['storage_path'] if not storage_path: raise BusinessException('storage support not enabled') file_path = Path(storage_path).joinpath(key) if not is_relative_to(file_path, storage_path): raise BusinessException('invalid file key: %s' % key) dirname = file_path.parent if not dirname.exists(): dirname.mkdir(parents=True) elif not dirname.is_dir(): raise BusinessException('dir exists: %s' % os.path.dirname(key)) elif file_path.exists(): raise BusinessException('file already exists: %s' % key) with file_path.open('wb+') as f: for chunk in file.chunks(): f.write(chunk) return success_response()
def login(self, request, *args, **kwargs): """ ## 用户登录 ``` Params: username string 用户名 password string 用户密码 Returns: object 用户数据结构 ``` """ serializer = forms.LoginForm(data=request.data, context=self.get_serializer_context()) serializer.is_valid(raise_exception=True) instance = serializer.save() serializer = self.get_serializer(instance) return success_response(serializer.data)
def manage_update(genericAPIView, request, partial, set_data): """全量更新数据""" print('进入全量更新了吗?') with transaction.atomic(): forward_relation_hand(genericAPIView.model, set_data) # partial = kwargs.pop('partial', False) instance = genericAPIView.get_object() old_instance = copy(instance) serializer = genericAPIView.get_validate_form(genericAPIView.action)( instance, data=set_data, partial=partial, context=genericAPIView.get_serializer_context(), ) serializer.is_valid(raise_exception=True) instance = genericAPIView.perform_update(serializer) serializer = genericAPIView.get_serializer(instance) if getattr(instance, '_prefetched_objects_cache', None): instance._prefetched_objects_cache = {} reverse_relation_hand(genericAPIView.model, set_data, instance) with transaction.atomic(): log.debug( 'sending Post Update signal with: model: %s, instance: %s', genericAPIView.model, instance, ) post_bsm_create.send( sender=genericAPIView.model, instance=instance, create=False, old_instance=old_instance, request=genericAPIView.request, scope='admin' ) return success_response(serializer.data)
def get_manage_menu(self, request, *args, **kwargs): """获取管理端的菜单配置""" if hasattr(settings, 'ADMIN_MENUS'): group_names = self.request.user.groups.values_list('name', flat=True) group_names = set(group_names) def map_menus(menus): return [{ **m, 'children': map_menus(m.get('children', [])) } for m in menus if 'groups' not in m or self.request.user.is_superuser or set(m['groups']) & group_names] return success_response(map_menus(settings.ADMIN_MENUS)) menutype = request.query_params.get('menutype', 'database') if menutype == 'database': return self._get_menu_from_database() if menutype == 'custom': return self._get_menu_from_custom() if menutype == 'autobuild': return self._get_menu_from_autobuild()
def client_create(genericAPIView, request, set_data): """ 这里校验表单和序列化类分开创建 原因:序列化类有可能嵌套 """ with transaction.atomic(): client_user_pip.add_login_user_data(genericAPIView, set_data) forward_relation_hand(genericAPIView.model, set_data) serializer = genericAPIView.get_validate_form(genericAPIView.action)( data=set_data ) serializer.is_valid(raise_exception=True) instance = genericAPIView.perform_create(serializer) reverse_relation_hand(genericAPIView.model, set_data, instance, detail=False) instance = genericAPIView.get_queryset().get(pk=instance.pk) # with transaction.atomic(): log.debug( 'sending Post Save signal with: model: %s, instance: %s', genericAPIView.model, instance, ) post_bsm_create.send( sender=genericAPIView.model, instance=instance, create=True, request=genericAPIView.request, old_instance=None, scope='client', ) # 如果有联合查询,单个对象创建后并没有联合查询, 所以要多查一次? serializer = genericAPIView.get_serializer( genericAPIView.get_queryset().get(pk=instance.pk) ) return success_response(serializer.data)
def permissions(self, request, *args, **kwargs): """获取当前用户的权限 """ return success_response(request.user.get_all_permissions())
def update_by_conditon(genericAPIView, set_fields): queryset = genericAPIView.filter_queryset(genericAPIView.get_queryset()) count = queryset.update(**set_fields) result = {'count': count} return success_response(result)
def get_admin(self, request, *args, **kwargs): self._load_bsm_admin_module() """获取 admin 配置""" data = get_app_admin_config() return success_response(data)
def admin(self, request, model_name): model, created = Admin.objects.get_or_create(model=model_name) model.config = dict(request.data) model.save() return success_response()
def get_web_settins(self, request, *args, **kargs): settings = get_settins() return success_response(settings)
def retrieve(genericAPIView, display_fields): """获取数据详情""" instance = genericAPIView.get_object() serializer = genericAPIView.get_serializer(instance) result = filter_display_fields(serializer.data, display_fields) return success_response(result)
def get_setting_config(self, request, *args, **kargs): settings = get_setting_config() return success_response(settings)
def block_view(request, block_id): block = Block.objects.get(id=block_id) data = None if block.component in component_resolver_map: data = component_resolver_map[block.component](block) return success_response(data)
def move(request, block_id): parent = request.data.get('parent', None) index = request.data['index'] services.move(block_id, parent, index) return success_response()
def logout(self, request, *args, **kwargs): """退出登录""" logout(request) return success_response()