Summary of learning and using django rest framework

Ⅰ. Precautions for creation and update using drf serialization
① When a field is not mandatory, the front end passes the field with the value "" empty string. At this time, it will report an error when it is created. There is no way to create it, and prompt this The field must pass a value.
Solution:
Add allow_blank=True to the sequence of the field

class ApplySerializer(serializers.ModelSerializer):
    last_version = serializers.CharField(required=False, allow_blank=True)
    approve_second = serializers.CharField(required=False, allow_blank=True)
    task_name = serializers.CharField(required=False, allow_blank=True)

②Serialization verification rules
1. Single field verification

class AvailAreaSerializer(serializers.ModelSerializer):
    name = serializers.CharField(
        validators=[UniqueValidator(
            queryset=AvailArea.objects.filter(is_delete=False), message=u'name已存在')]
    )

2. Check of multiple fields

class CodeConfigSerializer(serializers.ModelSerializer):
    bk_id = serializers.IntegerField(required=True)

    class Meta:
        model = CodeConfig
        fields = '__all__'
        validators = [
            UniqueTogetherValidator(
                queryset=CodeConfig.objects.all(),
                fields=(
                    'bk_type', 'bk_code',
                ),
                message='该类型编码已被使用'
            )
        ]

3. Custom validator

key_validator = RegexValidator(
    re.compile('^[_a-zA-Z0-9]+$'),
    message=_(u'请输入合法编码:英文数字及下划线'),
    code='invalid',
)

class DictDataSerializer(serializers.ModelSerializer):
    """数据字典数据项序列化"""

    key = serializers.CharField(required=True, error_messages={'blank': _(u"编码不能为空")}, max_length=LEN_LONG,
                                validators=[key_validator])

Order of weight: custom validator> single field validation> multiple field validation.
Note: The creation and update of the sequence list will be verified.
If you want to verify when creating, not when updating, personal suggestion method Rewrite the create method

class ProStoreConfigSerializer(serializers.ModelSerializer):

    class Meta:
        model = ProStoreConfig
        exclude = ('is_deleted',)

    def create(self, validated_data):
        if not result:
            raise serializers.ValidationError("制品库配置账户密码错误")
        queryset = ProStoreConfig.objects.all()
        if queryset.count():
            raise serializers.ValidationError("制品库配置已配置过了")
        else:
            instance = super(ProStoreConfigSerializer, self).create(validated_data=validated_data)
        return instance

Ⅱ. List filtering
1. Original field filtering

class BasicConfigFilter(django_filters.FilterSet):
    bk_biz_id = django_filters.Filter(name='bk_biz_id')
    bk_cluster_id = django_filters.Filter(name='bk_cluster_id')
    bk_module_id = django_filters.Filter(name='bk_module_id')

    class Meta:
        model = BasicConfig
        fields = '__all__'

2. The front-end single field corresponds to the back-end multi-field search.
An input box on the front-end, searches according to the recipient and mobile phone number, and passes into the receiver like the back-end

class NotifyLogFilter(django_filters.FilterSet):
    receiver = django_filters.Filter(method="filter_receiver", help_text="接收者匹配")

    class Meta:
        model = NotifyLog
        fields = '__all__'

    def filter_receiver(self, queryset, name, value):
        return queryset.filter(Q(receiver_name__icontains=value) | Q(receiver_phone__icontains=value))

3. Custom field filtering

class ApplyFilter(django_filters.FilterSet):
    bk_biz_id = django_filters.Filter(name='bk_biz_id', method="filter_by_bk_biz_id")
    bk_cluster_id = django_filters.Filter(name='bk_cluster_id', method="filter_by_bk_cluster_id")
    bk_module_id = django_filters.Filter(name='bk_module_id', method="filter_by_bk_module_id")
    now_version = django_filters.Filter(name="now_version", lookup_expr="icontains")
    creator = django_filters.Filter(name="creator", method="filter_by_creator")

    class Meta:
        model = Apply
        fields = '__all__'

    def filter_by_creator(self, queryset, name, value):
        if value == "admin":
            return queryset
        else:
            return queryset.filter(creator=value)

    def filter_by_bk_biz_id(self, queryset, name, value):
        return queryset.filter(basic_config__bk_biz_id=value)

    def filter_by_bk_cluster_id(self, queryset, name, value):
        return queryset.filter(basic_config__bk_cluster_id=value)

    def filter_by_bk_module_id(self, queryset, name, value):
        return queryset.filter(basic_config__bk_module_id=value)

Disadvantages: When some filter fields are obtained based on the request, there is no way to filter them. Secondly, the content of the search, you want to change the field value in the search result according to some field values ​​of the front end, it is not possible to change it, and the attribute value setting is not supported. Only the list method is rewritten
. Ⅲ. Add custom fields returned by serialization

class BasicConfig(Model):
    bk_biz_id = models.IntegerField(_('业务id'), null=True)
    bk_cluster_id = models.IntegerField(_('集群id'), null=True)
    bk_module_id = models.IntegerField(_('模块id'), null=True)
    publish_count = models.IntegerField(_('版本发布次数'), default=1)
    # 0 未启用 1 已启用
    status = models.BooleanField(_('启用状态'), default=0)

    class Meta:
        app_label = "configs"
        verbose_name = _("基础配置")
        verbose_name_plural = _("基础配置")

    @property
    def bk_biz_name(self):
        bk_biz_name, _, _ = self.get_topo_data
        return bk_biz_name

    @property
    def bk_cluster_name(self):

        _, bk_cluster_name, _ = self.get_topo_data
        return bk_cluster_name

    @property
    def bk_module_name(self):
        _, _, bk_module_name = self.get_topo_data
        return bk_module_name

    @property
    def get_topo_data(self):
        result = cache.get(CACHE_BIZ_TOPOLOGY_NAME)
        if not result:
            result = get_biz_topology()
        data = result.get(self.bk_biz_id)
        bk_biz_name = data.get('bk_inst_name')
        bk_cluster_name = None
        bk_module_name = None
        for i in data.get('child'):
            if i.get('bk_inst_id') == self.bk_cluster_id:
                bk_cluster_name = i.get('bk_inst_name')
            for j in i.get('child'):
                if j.get('bk_inst_id') == self.bk_module_id:
                    bk_module_name = j.get('bk_inst_name')
        return bk_biz_name, bk_cluster_name, bk_module_name

    @property
    def process_config(self):
        process_config_obj = ProcessConfig.objects.get(basic_config=self)
        return model_to_dict(process_config_obj)

    @property
    def version_config(self):
        version_config_obj = VersionConfig.objects.get(basic_config=self)
        return model_to_dict(version_config_obj)

    @property
    def approve_config(self):
        approve_config_obj = ApproveConfig.objects.get(basic_config=self)
        return model_to_dict(approve_config_obj)

    @property
    def bk_biz_code(self):
        biz_obj = CodeConfig.objects.filter(bk_id=self.bk_biz_id, bk_type=BIZ)
        return biz_obj[0].bk_code if biz_obj else None

    @property
    def bk_cluster_code(self):
        cluster_obj = CodeConfig.objects.filter(bk_id=self.bk_cluster_id, bk_type=CLUSTER)
        return cluster_obj[0].bk_code if cluster_obj else None

    @property
    def bk_module_code(self):
        module_obj = CodeConfig.objects.filter(bk_id=self.bk_module_id, bk_type=MODULE)
        return module_obj[0].bk_code if module_obj else None

    def code_version(self, date_time):
        code_version = \
            str(self.bk_biz_code) \
            + date_time + str(self.bk_module_code) + "." + str(self.publish_count)
        return code_version

Serialization returns custom fields, no need to re-representation (self, instance) method

class BasicConfigSerializer(serializers.ModelSerializer):
    bk_biz_id = serializers.IntegerField(required=True)
    bk_cluster_id = serializers.IntegerField(required=True)
    bk_module_id = serializers.IntegerField(required=True)

    class Meta:
        model = BasicConfig
        fields = ('id', 'bk_biz_id', 'bk_cluster_id', 'bk_module_id',
                  'bk_biz_name', 'bk_cluster_name', 'bk_module_name',
                  'bk_biz_code', 'bk_cluster_code', 'bk_module_code',
                  'process_config', 'version_config', 'approve_config',
                  'status', 'publish_count',
                  'updated_by', 'update_at',
                  'creator', 'create_at'
                  )
        validators = [
            UniqueTogetherValidator(
                queryset=BasicConfig.objects.filter(is_deleted=0),
                fields=(
                    'bk_biz_id', 'bk_cluster_id', 'bk_module_id'
                ),
                message='该系统环境已配置过发布流程'
            )
        ]

Ⅳ. Note that the custom method accepts parameters

class ProcessConfigViewSet(component_viewsets.ModelViewSet):
    serializer_class = ProcessConfigSerializer
    queryset = ProcessConfig.objects.all()

    @list_route(methods=['POST'], url_path='get_topology_data')
    def get_topology_data(self, request):
        """从缓存中获取业务拓扑"""
        # params = {'bk_biz_id': 2}
        params = request.data
        result = cache.get(CACHE_BIZ_TOPOLOGY_NAME)
        if not result:
            result = get_biz_topology()
        result = result.get(params.get('bk_biz_id')).get('child')
        return Response({"items": result, "count": len(result)})

Note: When the custom method is a POST request, use request.data to accept, do not use request.body to accept. When developing locally, request.body can accept the value passed by the front end. When deployed online, request .body is the value that cannot be retrieved from the front end, use request.data.
And the value of request.data is directly json, no need for json.loads().

Guess you like

Origin blog.csdn.net/qq_42631707/article/details/106915583