def高级之序列化的使用

序列化类的使用

使用序列化类实现五个接口功能,但是我们发现并没有做数据校验,也没有做反序列化,是我们自己手动去进反序列化,是我们自己使用for来进行拼接的,很不方便,我们可以使用一个drf自带的名叫序列化器,来完成

路由

urlpatterns = [
    path('admin/', admin.site.urls),
    path('publish/',views.PublishView.as_view()),
]

视图函数

class PublishView(APIView):
    def get(self, request):
        publish_list = Publish.objects.all()
        l = []
        for i in publish_list:
            l.append({'name': i.name, 'addr': i.addr, })
        return Response({'code': 100, 'msg': 'ok', 'results': l})

    def post(self, request):
        publish_list = Publish.objects.create(name=request.data.get('name'), addr=request.data.get('addr'))
        return Response({'code': 100, 'msg': 'ok', 'results': {'name': publish_list.name, 'price': publish_list.addr}})


class PublishDetail(APIView):
    def get(self, request, pk):
        book_list = Publish.objects.filter(id=pk).first()
        l = []
        for i in book_list:
            l.append({'name': i.name, 'price': i.price, })
        return Response({'code': 100, 'msg': 'ok', 'results': l})

    def delete(self, request, pk):
        book_list = Publish.objects.filter(id=pk).delete()
        return Response({'code': 100, 'msg': 'ok', 'results': book_list})

    def put(self, request):
        publish_list = Publish.objects.update(**request.data)
        return Response({'code': 100, 'msg': 'ok', 'results': {'name': publish_list.name, 'price': publish_list.addr}})

序列化类-Serializer

Django REST framework中的Serializer使用类来定义,须继承自rest_framework.serializers.Serializer。

from rest_framework import serializers

from app01.models import Publish


class PublishSerializer(serializers.ModelSerializer):
        name=serializers.CharField()
        addr=serializers.CharField()

        class Meta:
                model=Publish
                fields=['name','addr'] # 或指定所有__all__
  1. 建立一个名为Serializer的py文件·
  2. 写一个类,继承为Serializer.Serializer
  3. 在类中写需要序列化的字段
  4. 在视图类中使用,完成序列化

使用序列化类
导入我们自己的序列化器
from app01.Serializer import PublishSerializer


# 使用序列化类做序列化
class PublishView(APIView):
    def get(self, request):
        publish_list = Publish.objects.all()
        ser=PublishSerializer(instance=publish_list,many=True)
        return Response({'code': 100, 'msg': 'ok', 'results': ser.data})



class PublishDetail(APIView):
    def get(self, request, pk):
        publish_list = Publish.objects.filter(id=pk).first()
        ser=PublishSerializer(instance=publish_list)
        return Response({'code': 100, 'msg': 'ok', 'results': ser.data}))
  1. 如果序列化多个则需要加上many=Ture,

使用序列化类进行校验

三层校验:

  1. 字段的校验
  2. 局部钩子:校验失败,主动使用ValidationError抛出错误
  3. 全局钩子
from rest_framework import serializers
from rest_framework.exceptions import ValidationError

from app01.models import Publish, Books


class PublishSerializer(serializers.ModelSerializer):
    # 第一层:写需要序列化的字段名
    name = serializers.CharField()
    addr = serializers.CharField()
# 第二层 局部钩子
    def validate_name(self, name):
        if name.startswith('sb'):
            raise ValidationError('不能以sb开头')
        else:
            return name

    def validate_addr(self, addr):
        if 'sb'in addr:
            raise ValidationError("不能以sb开头")
        else:
            return addr


# 第三层全局钩子
    def validate(self, attrs):
        # attrs是校验过后的数据
        print(attrs)
        if attrs.get('name')[:3]==attrs.get('addr')[:3]:
            raise ValidationError("名称不能重复")

        return attrs


    class Meta:
        model = Publish
        fields = ['name', 'addr']  # 或指定所有__all__

全局钩子:def validate(self, attrs):

补充:attrs是已经校验过后的数据,如果前端传入了数据字段,但是我的类中并没有这个字段,那么最终校验过后的数据attrs中是没有前端传入的数据的,因为那条数据已经被删除了

put和post保存到数据库
class BookSerializer(serializers.ModelSerializer):
    name = serializers.CharField(max_length=18, min_length=3)
    price = serializers.IntegerField(max_value=999, min_value=10)
    pub_date = serializers.CharField(max_length=32)

    def validate_name(self, name):
        l = ['金瓶梅', 'sb', '傻逼']
        for i in l:
            if i in name:
                raise ValidationError('不能使用敏感字')
        return name

    def validate_price(self, price):
        if price < 10:
            raise ValidationError('价格不能小于10')
        elif price > 999:
            raise ValidationError('价格不能大于999')
        else:
            return price

    def validate(self, attrs):
        # attrs是校验过后的数据
        print(attrs)
        if attrs.get('pub_date')[:3] == attrs.get('name')[:3]:
            raise ValidationError("名称不能重复")
        return attrs

    def create(self, validated_data):
        # validated_data:校验过的数据,之前传入没有校验的数据,在这没有
        publish = Publish.objects.create(**validated_data)

        return publish  # 一定要返回publish对象,后续会拿着这个对象进行序列化,ser.



    def update(self, instance, validated_data):
        # 本方法
        # instance.name = validated_data.get('name')
        # instance.price = validated_data.get('price')
        # instance.pub_date = validated_data.get('pub_date')
        # instance.save() # book的对象save的对象保存到数据库中
        # 高级写法
        for item in validated_data:
            # setattr(books,'name','上海出版社')
            setattr(instance, item, validated_data[item])
            instance.save()

        return instance

    class Meta:
        model = Books
        fields = ['name', 'price', 'pub_date']

路由

class BookView(APIView):
    def get(self, request):
        book_list = Books.objects.all()
        ser = BookSerializer(instance=book_list, many=True)
        return Response({'code': 100, 'msg': 'ok', 'results': ser.data})

    def post(self, request):
        ser = BookSerializer(data=request.data)

        if ser.is_valid():

            ser.save()  # 使用序列化类进行保存,但是会报错,因为没有指定到哪一个表,所以需要重新写create
            print(ser.data)
            return Response({'code': 100, 'msg': 'ok', 'results': ser.data})
        else:
            print(ser.errors)
            return Response({'code': 100, 'msg': ser.errors})

    def delete(self, request, pk):
        book_list = Books.objects.filter(id=pk).delete()
        ser = BookSerializer(instance=book_list)
        return Response({'code': 100, 'msg': 'ok', 'results': ser.data})

    def put(self, request):
        book_list = Books.objects.update(**request.data)
        ser = BookSerializer(instance=book_list, data=request.data)

        if ser.is_valid():
            ser.save()
            print(ser.data)
            return Response({'code': 100, 'msg': '修改成功', 'results': ser.data})
        else:
            print(ser.errors)
            return Response({'code': 100, 'msg': ser.errors})
  1. instance:需要序列化的数据,如果是queryset对象,即便是一个数,也需要使用many=Ture
  2. serializers.data 转为字典格式,return Response(serializers.data)这样就实现序列化了
  3. 视图中使用新增与修改:
"""新增"""
serializers=Bookserializers(data=reuqest.data)
新增后执行视图函数中的is_valid:就是执行三层校验
成功后保存ser.save()
"""修改"""
ser = BookSerializer(instance=book_list, data=request.data)
is_valid()
ser.save()


    def update(self, instance, validated_data):
    	Book.objects.filer(pk=instanc).update(*validated_data)
        
        return instance



修改第二种方式

    def update(self, instance, validated_data):
    	Book.objects.filer(pk=instanc).update(**validated_data)
        
        return instance

常用的字段名

小技巧:如果跟表模型中对不上,可以直接使用CharField。

字段类型

字段

字段构造方式

BooleanField

BooleanField()

NullBooleanField

NullBooleanField()

CharField

CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)

EmailField

EmailField(max_length=None, min_length=None, allow_blank=False)

RegexField

RegexField(regex, max_length=None, min_length=None, allow_blank=False)

SlugField

SlugField(max_length=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9*-]+

URLField

URLField(max_length=200, min_length=None, allow_blank=False)

UUIDField

UUIDField(format='hex_verbose') format: 1) 'hex_verbose' 如"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex' 如 "5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"

IPAddressField

IPAddressField(protocol='both', unpack_ipv4=False, **options)

IntegerField

IntegerField(max_value=None, min_value=None)

FloatField

FloatField(max_value=None, min_value=None)

DecimalField

DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置

DateTimeField

DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)

DateField

DateField(format=api_settings.DATE_FORMAT, input_formats=None)

TimeField

TimeField(format=api_settings.TIME_FORMAT, input_formats=None)

DurationField

DurationField()

ChoiceField

ChoiceField(choices) choices与Django的用法相同

MultipleChoiceField

MultipleChoiceField(choices)

FileField

FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)

ImageField

ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)

ListField

ListField(child=, min_length=None, max_length=None)

DictField

DictField(child=)

选项参数:

参数名称

作用

max_length

最大长度

min_lenght

最小长度

allow_blank

是否允许为空

trim_whitespace

是否截断空白字符

max_value

最小值

min_value

最大值

通用参数:

参数名称

说明

read_only

表明该字段仅用于序列化输出,默认False

write_only

表明该字段仅用于反序列化输入,默认False

required

表明该字段在反序列化时必须输入,默认True

default

反序列化时使用的默认值

allow_null

表明该字段是否允许传入None,默认False

validators

该字段使用的验证器

error_messages

包含错误编号与错误信息的字典

label

用于HTML展示API页面时,显示的字段名称

help_text

用于HTML展示API页面时,显示的字段帮助提示信息

字段校验有四层:

  1. 字段自己
  2. validdators校验
  3. 局部钩子
  4. 全局钩子

序列化高级之soruce

soruce:用来修改返回字段的

1 修改字段,映射字段 publish_name表中不存在

publish_name = serializers.CharField(source='name')

2 修改字段,映射方法

sb_name是表模型中一个方法

name = serializers.CharField(source='sb_name')

3 修改字段,跨表查询

book表中可以链表查询

publish=models.ForeignKey(to='Publish.name')

序列化高级之返回字段

定制序列化返回字段格式

  1. 在表模型中写,在序列化中做映射,可以使用soruce

建表

from django.db import models


class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)

    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')

    def __str__(self):
        return self.name


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)

    def __str__(self):
        return self.name


class AuthorDetail(models.Model):
    telephone = models.BigIntegerField()
    birthday = models.DateField()
    addr = models.CharField(max_length=64)


class Publish(models.Model):
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=32)
    email = models.EmailField()

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = '出版社'
        verbose_name_plural = verbose_name

models

class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)

    publish_name = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    author = models.ManyToManyField(to='Author')
    def __str__(self):
        return self.name

    # def book_name(self):
    #     return self.name+'sb'

    # def publish_detail(self):
    #     return {'name': self.name, 'price': self.price, 'publish_name': self.publish_name.addr}
    #
    # def author_list(self):
    #     l=[]
    #     for i in self.author.all():
    #         l.append({'name':i.name, 'age':i.age})
    #
    #     return l

    # def publish_detail(self):
    #     return {'name': self.name, 'price': self.price, 'publish_name': self.publish_name.addr}
    #
    #
    # def author_list(self):
    #     l=[]
    #     for i in self.author.all():
    #         l.append({'name':i.name, 'age':i.age})
    #         return l
    #

    class Meta:
        verbose_name_plural='图书表'

view

class BookView(APIView):
    def get(self, request):
        obj = Book.objects.all()
        ser = BookSerializer(instance=obj, many=True)

        '''
        [
            {
                "name": "西游记",
                "price": "66.00",
                "publish_detail": {name:名字,city:城市},
                "authors_list":[{name:名字,age:19}]
            },
    
        ]
        
        '''
        return Response(ser.data)

序列化类

### 定制返回字段
class BookSerializer(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.CharField()
    #### 定制返回字段---》方案一:在表模型中写方法,在序列化类中做映射
    # publish_detail = serializers.CharField()  # publish_detail 会映射表模型中 publish_detail方法,方法返回值是 字典,强行用CharField字符串接收
    # publish_detail = serializers.DictField()  # publish_detail 会映射表模型中 publish_detail方法,方法返回值是 字典,用DictField接收
    # author_list = serializers.ListField()

    ###定制返回字段---》方案二:在序列化类中写  SerializerMethodField
    # 只要写了这个字段类SerializerMethodField,必须配合一个方法:get_字段名,这个方法返回什么,前端这个字段就显示什么
    publish_detail = serializers.SerializerMethodField()

    def get_publish_detail(self, obj):
        # 当前序列化到的book对象
        return {'name': obj.publish.name, 'city': obj.publish.city}

    author_list = serializers.SerializerMethodField()

    def get_author_list(self, obj):
        l = []
        for author in obj.authors.all():
            l.append({'name': author.name, 'age': author.age})
        return l

    book_name = serializers.SerializerMethodField()
	
    def get_book_name(self, obj):
        return obj.name + 'sb'

多表关联反序列化
# 反序列化保存
# 使用同一个序列化类会出现
	-序列化字段和反序列化字段不一致
   	-序列化字段
    	name
        price
        publish_detail
        author_list
    -反序列化字段:
    	name
        price
        publish
        author
        
    -如果是共同的,不需要额外处理
    -如果是不同的,需要通过字段参数控制
        read_only	表明该字段仅用于序列化输出,默认False,序列化过程
        write_only	表明该字段仅用于反序列化输入,默认False,反序列化过程

serializer
class BookSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=18, min_length=3, required=True) # 公共的
    price = serializers.IntegerField() # 公共的

    publish = serializers.IntegerField(write_only=True)# 反序列化
    author = serializers.ListField(write_only=True)# 反序列化

    publish_detail=serializers.SerializerMethodField(read_only=True) #序列化
    author_list = serializers.SerializerMethodField(read_only=True) #序列化




    def validate_name(self, name):
        if name.startswith('sb'):
            raise ValidationError("不能使用敏感字开头")
        return name


    def validate_price(self, price):
        if price <= 0:
            raise ValidationError("不能等于0")
        return price



    def validate(self, attrs):
            # attrs是校验过后的数据
        if attrs.get('name')[:3] == attrs.get('addr')[:3]:
            raise ValidationError("名称不能重复")

        return attrs


    def get_publish_detail(self, obj):
        # obj就是当前序列化到的
        return {'name': obj.publish_name.name ,'addr': obj.publish_name.addr}
        # 返回的' name':obj.publish.name, 'price':obj.publish.addr就相当于给了这个publish_detail


    def get_author_list(self,obj):
        l=[]
        for i in obj.author.all():
            l.append({'name':i.name, 'age':i.age})
        return l

    def create(self, validated_data):
        author=validated_data.pop('author')
        book = Book.objects.create(**validated_data)
        # book=Book.objects.create(name=validated_data['name'], price=validated_data['price'],publish_name=validated_data['publish_name'])
        book.author.add(*author)
        return book

ModelSerializer
  1. ModelSerializer跟表是一一对应的,r,需要重写create和update
  2. 之前写的Serializer.serializer跟表没有必然的联系
  3. 继承ModleSerializer可以少写很多代码
### 继承ModelSerializer--->少写代码
class BookSerializer(serializers.ModelSerializer):
    # name = serializers.CharField(max_length=18,min_length=3)  # 公共的
    # price = serializers.CharField()  # 公共的
    # publish = serializers.IntegerField(write_only=True)  # 只用来做反序列化
    # authors = serializers.ListField(write_only=True)  # 只用来做反序列化
    # 上述操作,通过 Meta实现了

    # 扩写的字段,也要在fields注册
    # 方式二:定制字段
    # publish_detail = serializers.SerializerMethodField(read_only=True)  # 只用来做序列化
    # author_list = serializers.SerializerMethodField(read_only=True)  # 只用来做序列化
    # def get_publish_detail(self, obj):
    #     return {'name': obj.publish.name, 'city': obj.publish.city}
    # def get_author_list(self, obj):
    #     l = []
    #     for author in obj.authors.all():
    #         l.append({'name': author.name, 'age': author.age})
    #     return l

    # 方式二:定制字段方式一
    # publish_detail = serializers.DictField(read_only=True)
    # author_list = serializers.ListField(read_only=True)
    class Meta:
        model = Book  # 写了这两句,会把表模型中Book,所有字段映射过来
        # fields='__all__'
        fields = ['name', 'price', 'publish', 'authors', 'publish_detail', 'author_list']
        extra_kwargs = {  # 给某个或某几个字段设置字段属性
            'name': {'max_length': 18, 'min_length': 3},
            'publish': {'write_only': True},
            'authors': {'write_only': True},
            'publish_detail': {'read_only': True},
            'author_list': {'read_only': True},
        }

    # 一般不需要写create和update了---》ModelSerializer帮咱们实现了
    # 局部钩子和全局钩子,该怎么写还怎么写

反序列化的源码分析
#1  执行 ser.is_valid() 就会执行 反序列化的校验---》字段自己--》局部钩子---》全局钩子
#2  入口是:ser.is_valid()---》BaseSerializer 找到了
	1 自己写的BookSerializer---》serializer.Serializer---->BaseSerializer 
    2 源码如下
    def is_valid(self, *, raise_exception=False):
        # self 是 ser对象---》自己写的BookSerializer的对象--》一开始没有
        # 一旦有了,就不执行了,优化is_valid被多次调用,只会走一次校验
        if not hasattr(self, '_validated_data'):
            try:
                # 一旦执行过,以后self中就有_validated_data
                # 接下来看self.run_validation(self.initial_data)
                self._validated_data = self.run_validation(self.initial_data)
            except ValidationError as exc:
                self._validated_data = {}
                self._errors = exc.detail
            else:
                self._errors = {}

        if self._errors and raise_exception:
            raise ValidationError(self.errors)

        return not bool(self._errors)
	  3 self.run_validation(self.initial_data)---》serializer.Serializer类的,不要按住ctrl点击,否则会进 Field 类,看错了
        
      4 serializer.Serializer类的run_validation
        def run_validation(self, data=empty):
            # data前端传入的--{"name":"张三","age":68}
            # value是---》前端传入的,字段自己校验通过的字典---{"name":"张三","age":68}
            value = self.to_internal_value(data) # 执行局部钩子
            try:
                self.run_validators(value) # 先不看,忽略掉
                # self 是 BookSerializer的对象,如果我们写了全局钩子,走我们自己的,如果没写,走父类的,父类的根本没做校验
                # value={"name":"张三","age":68}
                value = self.validate(value)# 执行全局钩子
            except (ValidationError, DjangoValidationError) as exc:
                raise ValidationError(detail=as_serializer_error(exc))

            return value
        
        
      5 全局钩子读完了:self 是 BookSerializer的对象,如果我们写了全局钩子,走我们自己的,如果没写,走父类的,父类的根本没做校验
      6 局部钩子:value = self.to_internal_value(data)--》Serializer类的
        # for循环着去BookSerializer的对象中反射  validate_字段名的方法,如果有就执行,没有就不执行
     def to_internal_value(self, data):
        for field in fields: # 序列化类中所有字段类的对象 name=CharField()
            # self 是BookSerializer类的对象
            # 去BookSerializer类中,反射  validate_field字段类的对象.field_name
            validate_method = getattr(self, 'validate_' + field.field_name, None)
            try:
                # 如果能拿到,说明咱么写了局部钩子
                if validate_method is not None:
                    # 执行局部钩子--》传入了当前字段的value值
                    validated_value = validate_method(validated_value)
            except ValidationError as exc:
               # 如果抛异常,会被捕获
                errors[field.field_name] = exc.detail
            except DjangoValidationError as exc:
                errors[field.field_name] = get_error_detail(exc)
            except SkipField:
                pass
            else:
                set_value(ret, field.source_attrs, validated_value)

        if errors:
            raise ValidationError(errors)

        return ret
        	
        
# #####读了局部和全局钩子的执行位置#####
# 保存,修改也好,都要用validated_data,它是最准确的

执行is_valid()就会执反序列化的校验

  • 字段自己的校验
  • 局部钩子
  • 全局钩子
  1. 查看源码从is_valid()中去查看,建议不使用ctrl点击查看,去ser,is_valid()的对象就是Booksserializer中查看-----没有找到,去父类的serializer中去寻找,没有继续去Serializer中找is_valiid(),没有继续去BaseSerializer中找,如果直接从ctrl中点击的话,可能直接跳转到不是BaseSerializer中了。
  2. 源码如下
 2 源码如下
    def is_valid(self, *, raise_exception=False):
        #判断self中有没有_validated_data
        if not hasattr(self, '_validated_data'):
            try:
                self._validated_data = self.run_validation(self.initial_data)
            except ValidationError as exc:
                self._validated_data = {}
                self._errors = exc.detail
            else:
                self._errors = {}

        if self._errors and raise_exception:
            raise ValidationError(self.errors)

        return not bool(self._errors)

逐步解析:

  1. if not hasattr(self, '_validated_data'):----判断self中有没有_validated_data
  • 其中的self就是ser对象,ser对象其实就是自己写的BookSerializer类的对象,因为从BookSerializer进入的
  • 一开始是没有_validated_data,(如果有就不会执行下面的hasattr,如果执行过is_valid(),那么之后的is_valid就不会执行了,多次调用也只会执行一次)所以执行下面的反射hasattr
  1. self._validated_data = self.run_validation(self.initial_data)------去Serializer中去找run_validation

不要直接ctrl直接进去找,不然找到了fild里就不对了

    def run_validation(self, data=empty):
        # 局部钩子
          value = self.to_internal_value(data)
        try:
            self.run_validators(value) 
            value = self.validate(value) # 全局钩子
            # 不符合直接捕获错误
        
        except (ValidationError, DjangoValidationError) as exc:
            raise ValidationError(detail=as_serializer_error(exc))

        return value

在run_validation中

全局钩子: 不符合直接捕获错误 value = self.validate(value) 全局钩子:self是BookSerializer的对象,如果自己写了钩子,那么就执行我们的,没写走父类,父类中直接返回了,并没有做校验

局部钩子

value = self.to_internal_value(data)-----是Serializer的

for循环去BookSerializer的对象去反射,validata_字段名的方法,如果有就执行,

  • value就前端传入的数据
    def to_internal_value(self, data):

        for field in fields:
            validate_method = getattr(self, 'validate_' + field.field_name, None)
            #去BookSerializer中反射validate_加上字段名 :  validate_field字段类的对象.field_name
            primitive_value = field.get_value(data)
            try:
                # 如果能拿到,说明写了局部钩子,否则抛出异常
                validated_value = field.run_validation(primitive_value)
                if validate_method is not None:
                    validated_value = validate_method(validated_value)
            except ValidationError as exc:
                #如果抛异常,会被捕获到,放入下面errors中
                errors[field.field_name] = exc.detail
            except DjangoValidationError as exc:
                errors[field.field_name] = get_error_detail(exc)
            except SkipField:
                pass
            else:
                set_value(ret, field.source_attrs, validated_value)

        if errors:
            raise ValidationError(errors)
    	# 最终返回
        return ret

for field in fields: 序列化类中所有字段的对象:

validate_method = getattr(self, 'validate_' + field.field_name, None)

去BookSerializer中反射validate_加上字段名 : validate_field字段类的对象.field_name

你可能感兴趣的:(python,windows,开发语言)