Ⅰ.走drf序列化创建、更新注意事项
①当某一字段不是必传的时候,前端传了该字段,值是""空字符串,这个时候创建就会报错,没有办法进行创建,提示这个字段必须要传值
解决方法:
对该字段的序列加入 allow_blank=True
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)
②序列化校验规则
1.单个字段的校验
class AvailAreaSerializer(serializers.ModelSerializer):
name = serializers.CharField(
validators=[UniqueValidator(
queryset=AvailArea.objects.filter(is_delete=False), message=u'name已存在')]
)
2.多个字段的校验
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.自定义验证器
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])
权重顺序:自定义验证器 > 单个字段的验证 > 多个字段的验证
注意事项:执行序列表创建、更新都会进行校验的
若想创建时进行校验,更新时不校验,个人建议的方法进行create方法的重写
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
Ⅱ.列表的过滤
1.原字段过滤
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.前端单字段对应后端多个字段搜索
前端一个输入框,根据接收人、手机号搜索,像后端传入receiver
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.自定义字段过滤
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)
弊端:当某些过滤字段,是根据request进行获取的,是没有办法进行过滤的。其次搜索的内容,你想根据前端的某些字段值变更搜索结果中的字段值,是无法进行变更的,不支持设置属性值,只有重写list方法
Ⅲ.增加序列化返回的自定义字段
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
序列化返回自定义字段,不需要重新to_representation(self, instance)方法
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='该系统环境已配置过发布流程'
)
]
Ⅳ.自定义方法接受参数注意
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)})
注意:当自定义方法是POST请求时,用request.data进行接受,不要采用request.body接受,在本地开发时,request.body可以接受到前端的传值,当部署到线上的时候,request.body是无法取到前端的传值,用request.data。
并且request.data取值,直接就是json,不需要再json.loads()。