Пример #1
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()
Пример #2
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()
Пример #3
0
 def basebone_get_statistics_config(self):
     """
     FIXME: 获取统计配置,暂时只支持管理端,客户端待完成
     """
     if self.end_slug == const.MANAGE_END_SLUG:
         # 如果是管理端的配置,直接使用 admin 中的配置
         admin_class = self.get_bsm_model_admin()
         if admin_class:
             config = getattr(admin_class, admin.BSM_STATISTICS, None)
             if not config:
                 raise exceptions.BusinessException(
                     error_code=exceptions.BSM_NOT_STATISTICS_CONFIG
                 )
             return config
         raise exceptions.BusinessException(
             error_code=exceptions.BSM_CAN_NOT_FIND_ADMIN
         )
Пример #4
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)
Пример #5
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
Пример #6
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)
         )
Пример #7
0
 def proxy(*args, **kwargs):
     signature = inspect.signature(func)
     required = {
         k
         for k, p in list(signature.parameters.items())[len(args):]
         if p.default == Parameter.empty and p.kind in
         [Parameter.KEYWORD_ONLY, Parameter.POSITIONAL_OR_KEYWORD]
     }
     lack = required - kwargs.keys()
     if lack:
         raise exceptions.BusinessException(
             exceptions.PARAMETER_FORMAT_ERROR, "、".join(lack) + " 必填")
     return func(*args, **kwargs)
Пример #8
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
Пример #9
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
Пример #10
0
def forward_one_to_many(field, value, update_data):
    """处理正向的一对多关系

    - 如果数据不是一个字典,则直接返回
    - 如果数据是字典,字典中没有包含主键,则对字典中的数据进行创建
    - 如果数据是字典,字典中包含主键,则对主键指定的数据进行更新
    """
    if not isinstance(value, dict):
        return

    model, key = field.related_model, field.name
    pk_field_name = model._meta.pk.name

    # 如果传进来的数据不包含主键,则代表是创建数据
    if pk_field_name not in value:
        # FIXME: 嵌套处理
        forward_relation_hand(model, value)

        serializer = create_serializer_class(model, allow_one_to_one=True)(data=value)
        serializer.is_valid(raise_exception=True)
        instance = serializer.save()
        update_data[key] = instance.pk
        reverse_relation(model, value, instance)
        return update_data

    # 如果传进来的数据包含主键,则代表是更新数据
    instance = model.objects.filter(**{pk_field_name: value[pk_field_name]}).first()
    if not instance:
        raise exceptions.BusinessException(
            error_code=exceptions.OBJECT_NOT_FOUND,
            error_data=f"{key}: {value} 指定的主键找不到对应的数据",
        )

    # FIXME: 嵌套处理
    forward_relation_hand(model, value)

    serializer = create_serializer_class(model, allow_one_to_one=True)(
        instance=instance, data=value, partial=True
    )
    serializer.is_valid(raise_exception=True)
    serializer.save()
    update_data[key] = value[pk_field_name]
    reverse_relation(model, value, instance)
    return update_data
Пример #11
0
def reverse_one_to_one(field, value, instance):
    model = field.related_model
    pk_field = model._meta.pk
    if isinstance(value, dict):
        remote_field_name = field.remote_field.name

        Serializer = create_serializer_class(model, allow_one_to_one=True)
        value = forward_relation_hand(model, value)
        if pk_field.name not in value:
            model.objects.filter(**{field.remote_field.name: instance}).delete()
            value[field.remote_field.name] = instance.pk
            serializer = Serializer(data=value)
        else:
            pk_value = pk_field.to_python(value[pk_field.name])
            filter_params = {
                pk_field.name: pk_value,
                field.remote_field.name: instance,
            }
            obj = model.objects.filter(**filter_params).first()
            if not obj:
                raise exceptions.BusinessException(
                    error_code=exceptions.OBJECT_NOT_FOUND,
                    error_data=f"{model}指定的主键[{pk_value}]找不到对应的数据",
                )

            value[remote_field_name] = instance.pk
            serializer = Serializer(instance=obj, data=value, partial=True)

        serializer.is_valid(raise_exception=True)
        obj = serializer.save()
        reverse_relation(field.related_model, value, obj)
    else:
        if value is None:
            model.objects.filter(**{field.remote_field.name: instance.pk}).delete()
        else:
            model.objects.filter(
                **{field.field_name: pk_field.to_python(value)}
            ).update(**{field.remote_field.name: instance.pk})
Пример #12
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))
Пример #13
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,
        )
Пример #14
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)
Пример #15
0
def forward_many_to_many(field, value, update_data):
    """处理正向的多对多关系

    - 如果多对多关系的字段的值不是列表,则不做任何处理
    - 如果传进来的值是一个列表,则不做任何处理

    上面的后续校验扔给下一步的创建或者更新的表单验证
    """
    if not (isinstance(value, list) and value):
        return

    key = field.name
    pure_data, object_data = [], []
    for item in value:
        object_data.append(item) if isinstance(item, dict) else pure_data.append(item)

    # 如果不包含对象数据,则不做任何处理
    if not object_data:
        return

    model = field.related_model
    pk_field_name = model._meta.pk.name

    # 筛选更新的列表和创建的列表
    create_list, update_list = [], []
    for item in object_data:
        update_list.append(item) if pk_field_name in item else create_list.append(item)

    if create_list:
        create_ids = []
        for item_data in create_list:

            # FIXME: 嵌套处理
            forward_relation_hand(model, item_data)

            serializer = create_serializer_class(model, allow_one_to_one=True)(
                data=item_data
            )
            serializer.is_valid(raise_exception=True)
            create_ids.append(serializer.save().pk)
        pure_data += create_ids

    if update_list:
        update_data_map = {item[pk_field_name]: item for item in update_list}
        filter_params = {f"{pk_field_name}__in": update_data_map.keys()}
        queryset = model.objects.filter(**filter_params)

        # 检查查询出的数据是否和传入的 id 长度一致
        if queryset.count() != len(update_list):
            raise exceptions.BusinessException(
                error_code=exceptions.OBJECT_NOT_FOUND,
                error_data=f"{key}: {update_list} 存在不合法的数据",
            )

        for instance in queryset.iterator():
            item_data = update_data_map.get(getattr(instance, pk_field_name, None))

            # FIXME: 嵌套处理
            forward_relation_hand(model, item_data)
            serializer = create_serializer_class(model, allow_one_to_one=True)(
                instance=instance, data=item_data, partial=True
            )
            serializer.is_valid(raise_exception=True)
            serializer.save()
        pure_data += update_data_map.keys()
    update_data[key] = pure_data
    return update_data
Пример #16
0
def reverse_one_to_many(field, value, instance, detail=True):
    """处理反向字段的多对一的数据

    对于此种场景,数据格式是包含对象的列表或者已经存在对象的主键

    注意事项:
    - 创建时,不能传入包含主键的数据

    场景描述:
        class AModel(models.Model):
            pass

        class BModel(models.Model):
            a = models.ForgignKey(AModel)
            name = models.CharField()

    AModel 请求数据时,字段中包含 bmodel 对象如下数据格式:

        FIXME: 创建和更新时可以传入:
            bmodel: [
                {
                    name: 'xxxx'
                }
            ]

        或者

        FIXME: 只有更新时可以传入包含主键的数据:
            bmodel: [
                {
                    'id': xxxx,
                    'name': 'update xxxxx'
                }
            ]
    """
    model, key = field.related_model, field.name
    pk_field_name = model._meta.pk.name

    # 进行到这一步,说明对应的键时存在的,对于反向字段,就直接校验数据
    if not isinstance(value, list):
        raise exceptions.BusinessException(
            error_code=exceptions.PARAMETER_FORMAT_ERROR,
            error_data=f"{key}: {value} 只能是列表",
        )

    # 这里需要判断创造和更新
    if detail:
        # 如果是更新,如果传入空的数据,则删除掉对应的数据
        if not value:
            try:
                related_name = meta.get_relation_field_related_name(
                    model, field.remote_field.name
                )
                if related_name:
                    relation = getattr(instance, related_name[0], None)
                    if relation:
                        relation.all().delete()
                        return
            except Exception as e:
                raise exceptions.BusinessException(
                    error_code=exceptions.PARAMETER_FORMAT_ERROR, error_data=str(e)
                )
    else:
        # 如果是创建,如果传进来的值为空
        if not value:
            return

    value = forward_relation_hand(model, value)

    pure_id_list, object_data_list = [], []
    for item in value:
        if isinstance(item, dict):
            object_data_list.append(item)
            if pk_field_name in item:
                pure_id_list.append(item[pk_field_name])
        else:
            pure_id_list.append(item)

    if object_data_list:
        # TODO: 创建时,不能传入包含主键的数据
        for item in object_data_list:
            if not detail and pk_field_name in item:
                raise exceptions.BusinessException(
                    error_code=exceptions.PARAMETER_BUSINESS_ERROR,
                    error_data=f"{key}: {value} 当前为 create 操作,不能传入包含主键的数据",
                )

        for item_value in object_data_list:
            if pk_field_name in item_value:
                # 此时说明是更新的数据
                pk_value = model._meta.pk.to_python(item_value[pk_field_name])

                filter_params = {
                    pk_field_name: pk_value,
                }
                obj = model.objects.filter(**filter_params).first()
                if not obj:
                    raise exceptions.BusinessException(
                        error_code=exceptions.OBJECT_NOT_FOUND,
                        error_data=f"{key}: {value} 指定的主键找不到对应的数据",
                    )

                item_value[field.remote_field.name] = instance.pk
                serializer = create_serializer_class(model, allow_one_to_one=True)(
                    instance=obj, data=item_value, partial=True
                )
                serializer.is_valid(raise_exception=True)
                serializer.save()
            else:
                # 如果传进来的数据不包含主键,则代表是创建数据
                item_value[field.remote_field.name] = instance.pk
                serializer = create_serializer_class(model, allow_one_to_one=True)(
                    data=item_value
                )
                serializer.is_valid(raise_exception=True)
                obj = serializer.save()
                if detail:
                    pure_id_list.append(getattr(obj, pk_field_name))

            reverse_relation(field.related_model, item_value, obj)

    # 如果是更新,则删除掉对应的数据
    if detail and pure_id_list:
        pure_id_list = [model._meta.pk.to_python(item) for item in pure_id_list]
        relation = meta.get_relation_field_related_name(model, field.remote_field.name)

        if relation:
            model.objects.filter(pk__in=pure_id_list).update(
                **{relation[1].name: instance}
            )
            getattr(instance, relation[0]).exclude(
                **{f"{pk_field_name}__in": pure_id_list}
            ).delete()
    elif pure_id_list:
        # 如果是创建,则需要创建对应的数据
        pure_id_list = [model._meta.pk.to_python(item) for item in pure_id_list]
        relation = meta.get_relation_field_related_name(model, field.remote_field.name)
        getattr(instance, relation[0]).add(
            *model.objects.filter(**{f"{pk_field_name}__in": pure_id_list})
        )