• django-rest-framework 基础二 序列化器和路由


    django-rest-framework 基础二 序列化器和路由

    1. 序列化器

    1. 序列化,序列化器会把模型对象(qs,book)转换成字典,经过response以后变成json字符串
    2. 反序列化,把客户端发(前端)送过来的数据,经过request以后变成字典(data),序列化器可以把字典转成模型-->存到数据库中
    3. 反序列化,完成数据校验功能---》前端传入的数据是否合法,长度够不够等等, 进行数据校验
    

    1.1 Serializer的使用

    使用序列化器完成增删改查接口

    准备数据,modles.py

    from django.db import models
    
    # Create your models here.
    class Book(models.Model):
        name = models.CharField(max_length=128)
        auth = models.CharField(max_length=128)
        price = models.DecimalField(decimal_places=2, max_digits=5)
    """
    增加一个书籍表
    数据库迁移:
    python3 manage.py makemigrations
    python3 manage.py migrate
    
    
    
    如果之前这些步骤做过,可以忽略
    """
    
    

    序例化文件serializers.py

    from rest_framework import serializers
    from drftest.models import Book
    
    class BookSerializer(serializers.Serializer):
        id = serializers.IntegerField(read_only=True)
        name = serializers.CharField(min_length=3)    # 不但序列化,而且限制最小长度不能小于3
        auth = serializers.CharField()
        price = serializers.DecimalField(decimal_places=2, max_digits=5)
        
         def create(self, validated_data):
            # validated_data校验过后的数据
            book = Book.objects.create(**validated_data)
            return book # 返回新增的对象
    
    
        def update(self, instance, validated_data):
            # instance为要修改的对象
            # validated_data校验过后的数据
            instance.name = validated_data.get('name')
            instance.auth = validated_data.get('auth')
            instance.price = validated_data.get('price')
            instance.save()  # 模型对象自带的save,保存到数据库中(必须要save,否则只修改了数据,但没有保存到数据库里)
            return instance
        
        
        """ 
        必须要重写create和update 因为save里只是定义了,但没具体实现,因为不知道具体存到哪个表中,所以要在序列化类中实现
        
    	def update(self, instance, validated_data):
            raise NotImplementedError('`update()` must be implemented.')
    
        def create(self, validated_data):
            raise NotImplementedError('`create()` must be implemented.')    
        """
        
    

    视图函数views.py

    from drftest.serializers import BookSerializer
    from drftest.models import Book
    
    from rest_framework.views import APIView
    from rest_framework.response import  Response
    
    class BookView(APIView):
        # 查全部的数据
        def get(self,request):
            book_list = Book.objects.all()
            res = BookSerializer(instance=book_list, many=True)
            return Response(res.data)
    	# 新增一条的数据
        def post(self, request):
            res = BookSerializer(data=request.data)
            if res.is_valid(): # 校验数据
                res.save()  # 校验通过保存数据,保存时要重写create 方法,在序列化文件里
                return Response(res.data)
            return Response({"code" : 1001, "msg" : "数据验证失败", "error" : res.errors})
        
    class BookViewDetail(APIView):
         # 查某一条的数据
        def get(self,request,pk):
            book_list = Book.objects.filter(pk=pk).first()
            res = BookSerializer(instance=book_list)
            return Response(res.data)
    	# 修改某一条的数据
        def put(self,request, pk):
            book_list = Book.objects.filter(pk=pk).first()
            # 既有instance,又有data,表示修改
            res = BookSerializer(instance=book_list, data=request.data)
            if res.is_valid(): # 校验数据
                res.save() # 校验通过保存时要重写update 方法,在序列化文件里
                return  Response(res.data)
            return Response({"code": 1001, "msg": "数据验证失败", "error": res.errors})
    
    	# 删除某一条的数据
        def delete(self, request, pk):
            res = Book.objects.filter(pk=pk).delete()
            print(res)
            return Response({"code": 1002, "msg": "数据删除成功"})
    
    

    路由urls.py

    from django.contrib import admin
    from django.urls import path
    from drftest import  views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('books/', views.BookView.as_view()),
        path('books/<int:pk>/', views.BookViewDetail.as_view()),
    ]
    

    示例:

    查全部数据

    image-20220331020716677

    查某一条数据:

    image-20220331020746762

    新增一条数据:

    image-20220331020917838

    修改一条数据(修改价格为12.9)

    image-20220331021012803

    删除一条数据:

    image-20220331021131227

    总结

    • 第一步:写一个类:必须继承drf中的Serializer及其子类

    • 第二步:在类中写要序列化的字段-->要序列化哪些,就写哪些,不序列化的不写

    • 第三步:使用序列化类,视图类中用

      得到序列化类对象 对象.data,通过Response返回给前端

    1.2 序列化器中的字段类型

    字段 字段构造方式
    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(maxlength=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=)

    其中ListFieldDictFieldmodels中没有的类型,在反序列化时,如果前端传入列表或字典可以使用这两字段进行反序列化。

    1.2.1 字段参数

    写在字段类中的参数

    选项参数:

    参数名称 作用
    max_length 最大长度(CharField)
    min_lenght 最小长度(CharField)
    allow_blank 是否允许为空(CharField)
    trim_whitespace 是否截断空白字符(CharField)
    max_value 最小值 (IntegerField)
    min_value 最大值(IntegerField)

    通用参数

    参数名称 说明
    read_only 表明该字段仅用于序列化输出,默认False
    write_only 表明该字段仅用于反序列化输入,默认False
    required 表明该字段在反序列化时必须输入,默认True
    default 反序列化时使用的默认值
    allow_null 表明该字段是否允许传入None,默认False
    validators 该字段使用的验证器(不太用)
    error_messages 包含错误编号与错误信息的字典
    label 用于HTML展示API页面时,显示的字段名称
    help_text 用于HTML展示API页面时,显示的字段帮助提示信息

    通用参数中重点的两个:

    """
    read_only:表明该字段仅用于序列化输出,默认False
      如果 read_only=True,这个字段只用来做序列化
    		把对象---》json给前端
        
    write_only:表明该字段仅用于反序列化输入,默认False
      如果 write_only=True,这个字段只用来做反序列化
    		前端json---》存到数据库
        
        
    什么都不写,表示既序列化,又反序列化
    read_only=True 序列化给前端,前端看到的字段,但前端传数据的时候可以不传这个对应的字段
    write_only=True 反序列化时,前端需要传什么过的字段
    但一个字段里不能即写read_only=True又写write_only=True。
    
    示例:
    	id = serializers.CharField(read_only=True)
        name=serializers.CharField(max_length=32,min_length=3,)
        auth=serializers.CharField(write_only=True)
    """
    

    1.3 序列化时,定制序列化的字段

    例如定制一个price_info字段

    方法一:在序列化类中写

    class BookSerializer(serializers.Serializer):
    	...
        price_info = serializers.SerializerMethodField()
        # 使用SerializerMethodField方法,下面必须要写一个 以get_开头后跟自定义字段名的函数
        def get_price_info(self,obj):
            return "price is " + str(obj.price)
        
    	...
        
    
    # 只在序列化中增加,其他的内容不变
    """
    class ExampleSerializer(self):
    	extra_info = SerializerMethodField()
    
    	def get_extra_info(self, obj):
    		return ...  # Calculate some data to return.
    
    """
    

    image-20220331023347330

    方法二:在models中写方法

    """models.py"""
    from django.db import models
    
    # Create your models here.
    
    class Book(models.Model):
        name = models.CharField(max_length=128)
        auth = models.CharField(max_length=128)
        price = models.DecimalField(decimal_places=2, max_digits=5)
    
        #以下为新增的自定义的字段
        @property
        def price_info(self):
            return "price is " + str(self.price)
        
    # 在序列化类中
    """Serializers.py"""
    
    class BookSerializer(serializers.Serializer):
        ...
        #models里定义,序列化中使用 read_only=True 只有序列化中使用
        price_info = serializers.CharField(read_only=True)
        ...
        
    # 其他的内容不变
    

    image-20220331023846593

    上面的两个方法的效果一样。

    1.4 局部勾子和全局勾子

    验证顺序:

    先走字段选项参数的规则,再走局部钩子,最后是走全局钩子
    

    1.4.1 字段选项参数的规则:

    """serializers.py"""
    
    from rest_framework import serializers
    from drftest.models import Book
    
    class BookSerializer(serializers.Serializer):
        id = serializers.IntegerField(read_only=True)
        name = serializers.CharField(min_length=3)    # 不但序列化,而且限制最小长度不能小于3
        auth = serializers.CharField()
        price = serializers.DecimalField(decimal_places=2, max_digits=5)
        
    """
    字段选项参数的规则:
    	name = serializers.CharField(min_length=3)  限制书籍名字最小长度不能小于3
    	price = serializers.DecimalField(decimal_places=2, max_digits=5)
    """
    

    书籍名字为不符合规范

    image-20220331025112403

    书籍名字为不符合规范并价钱也不符合

    image-20220331025203422

    1.4.2 局部勾子验证

    class BookSerializer(serializers.Serializer):
    	def create(self, validated_data):pass
        def update(self, instance, validated_data):
        # 局部钩子,只验证某一个字段
        # 要验证哪个字段, 必须要validate_开后,后面跟字段名,如:validate_name
        def validate_name(self,attr):
            if attr.startswith('xx'):
                raise ValidationError("名字不能以xx开头")
            else:
                return attr  # 没有问题,正常返回
    
    

    验证(虽然符合字段的规范,但不符合局部勾子的规范)

    image-20220331025646837

    1.4.3 全部勾子

    class BookSerializer(serializers.Serializer):
    	def create(self, validated_data):pass
        def update(self, instance, validated_data):
            
        def validate(self, attrs):
            # attrs校验过后的数据
            if attrs.get('name') == attrs.get('auth'):
                raise ValidationError('作者名不能等于书名')
            else:
                return attrs
    

    image-20220331025920412

    1.5 ModelSerializer模型类序列化器

    上面使用的序列化器写出的接口,在新增和修改的时候必须要重写createupdate方法,可以使用ModelSerializer跟表模型做绑定,就不需要重写createupdate方法了。

    为了和之前写的做区分,继承ModelSerializer类的为第二个版本_v2

    序列化serializers.py

    class BookSerializer_v2(serializers.ModelSerializer):
        class Meta:
            model = Book
            # fields = '__all__'  # 拿全部字段
            fields = ['id','name','auth', 'price','price_info']
    
    
    	# 没有create 和updata方法了
        # 局部勾子
        def validate_name(self, attr):
            if attr.startswith('YY'):
                raise ValidationError("名字不能以YY开头")
            else:
                return attr  # 没有问题,正常返回
        # 全局勾子
        def validate(self, attrs):
            # attrs校验过后的数据
            if attrs.get('name') == attrs.get('auth'):
                raise ValidationError('作者名不能等于书名')
            else:
                return attrs
    

    视图views.py

    class BookView_v2(APIView):
        def get(self,request):
            book_list = Book.objects.all()
            res = BookSerializer_v2(instance=book_list, many=True)
            return Response(res.data)
    
        def post(self, request):
            res = BookSerializer_v2(data=request.data)
            if res.is_valid():
                res.save()
                return Response(res.data)
            return Response({"code" : 1001, "msg" : "数据验证失败", "error" : res.errors})
    
    
    
    
    class BookViewDetail_v2(APIView):
        def get(self, request, pk):
            book_list = Book.objects.filter(pk=pk).first()
            res = BookSerializer_v2(instance=book_list)
            return Response(res.data)
    
        def put(self, request, pk):
            book_list = Book.objects.filter(pk=pk).first()
            # 既有instance,又有data,表示修改
            res = BookSerializer_v2(instance=book_list, data=request.data)
            if res.is_valid():
                res.save()
                return Response(res.data)
            return Response({"code": 1001, "msg": "数据验证失败", "error": res.errors})
    
        def delete(self, request, pk):
            res = Book.objects.filter(pk=pk).delete()
            print(res)
            return Response({"code": 1002, "msg": "数据删除成功"})
    
    

    路由urls.py

    rom django.contrib import admin
    from django.urls import path
    from drftest import  views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('books/', views.BookView.as_view()),
        path('books/<int:pk>/', views.BookViewDetail.as_view()),
        path('books_v2/', views.BookView_v2.as_view()),
        path('books_v2/<int:pk>/', views.BookViewDetail_v2.as_view()),
    ]
    

    访问新接口:

    image-20220331030913817

    修改一条数据

    原数据:

    image-20220331030958820

    修改价格为:12.8

    image-20220331031122342

    使用ModelSerializer不用写createupdate方法,依然可以修改和新增。

    1.5.1 增加额外的参数

    上面的方法不用写createupdate方法了,但是如果想在使用字段选项参数,没办法直接传参数了。

    解决方法:

    extra_kwargs = {'字段名':{参数:值}}
    

    序列化serializers.py

    class BookSerializer_v2(serializers.ModelSerializer):
        class Meta:
            model = Book
            # fields = '__all__'  # 拿全部字段
            fields = ['id','name','auth', 'price','price_info']
    
            # 使用字段选项参数
            extra_kwargs = {
                'id':{'read_only':True},
                'price_info':{'read_only':True},
                'name':{'min_length':3,'max_length':5}
    
            }
    
    
        def validate_name(self, attr):
            if attr.startswith('YY'):
                raise ValidationError("名字不能以YY开头")
            else:
                return attr  # 没有问题,正常返回
        # 全局勾子
        def validate(self, attrs):
            # attrs校验过后的数据
            if attrs.get('name') == attrs.get('auth'):
                raise ValidationError('作者名不能等于书名')
            else:
                return attrs
            
            
            
    # 如果也想在这里面定制字段,比如像上面一样,增加price_info,
    # price_info,它不是数据库中字段,但也要在fields中注册
     fields = ['id','name','auth', 'price','price_info']
    
    # price_info字段的增加也只能使用在models.py中写方法了
    """models.py代码:"""
    
    from django.db import models
    
    # Create your models here.
    
    class Book(models.Model):
        name = models.CharField(max_length=128)
        auth = models.CharField(max_length=128)
        price = models.DecimalField(decimal_places=2, max_digits=5)
        @property
        def price_info(self):
            return "price is " + str(self.price)
    

    1.6 序列化多表操作

    拿书籍的数据时,不但要拿基本信息,还要拿对应的出版社和作者

    序列化serializers.py

    from rest_framework import serializers
    from drftest.models import Book
    from django.core.exceptions import ValidationError
    
    class BookSerializer_v2(serializers.ModelSerializer):
        class Meta:
            model = Book
            # fields = '__all__'  # 拿全部字段
            fields = ['id','name','auth', 'price','price_info','publish_list','auth_list']
    
            extra_kwargs = {
                'id':{'read_only':True},
                'price_info':{'read_only':True},
                'price':{'write_only':True},
                'name':{'min_length':3,'max_length':5},
                'publish_list':{'read_only':True},
                'auth_list':{'read_only':True},
    
            }
    
    
        def validate_name(self, attr):
            if attr.startswith('YY'):
                raise ValidationError("名字不能以YY开头")
            else:
                return attr  # 没有问题,正常返回
        # 全局勾子
        def validate(self, attrs):
            # attrs校验过后的数据
            if attrs.get('name') == attrs.get('auth'):
                raise ValidationError('作者名不能等于书名')
            else:
                return attrs
    
    

    models.py

    from django.db import models
    
    # Create your models here.
    
    class Book(models.Model):
        name = models.CharField(max_length=128)
        auth = models.CharField(max_length=128)
        price = models.DecimalField(decimal_places=2, max_digits=5)
        publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE,default=1)
        bookToAuth = models.ManyToManyField(to='Authors')
    
        @property
        def price_info(self):
            return "price is " + str(self.price)
    
        @property
        def publish_list(self):
            #单条数据
            return {'name':self.publish.name,"address":self.publish.address,'phone':self.publish.phone}
    
        @property
        def auth_list(self):
            # 多条数据
            l = []
            for auth in self.bookToAuth.all():
                l.append({"name":auth.name, 'city':auth.city})
            return l
    
    
    class Publish(models.Model):
        name = models.CharField(max_length=255)
        address = models.CharField(max_length=255)
        phone = models.CharField(max_length=20)
    
    class Authors(models.Model):
        name = models.CharField(max_length=128)
        city = models.CharField(max_length=128)
        authdetail = models.ForeignKey(to='AuthDetail', on_delete=models.CASCADE,default=1)
    
    
    class AuthDetail(models.Model):
        address = models.CharField(max_length=255)
        phone = models.CharField(max_length=20)
    
    

    views.py

    class BookView_v2(APIView):
        def get(self,request):
            book_list = Book.objects.all()
            res = BookSerializer_v2(instance=book_list, many=True)
            return Response(res.data)
    
        def post(self, request):
            res = BookSerializer_v2(data=request.data)
            if res.is_valid():
                res.save()
                return Response(res.data)
            return Response({"code" : 1001, "msg" : "数据验证失败", "error" : res.errors})
        
        
        
    class BookViewDetail_v2(APIView):
        def get(self, request, pk):
            book_list = Book.objects.filter(pk=pk).first()
            res = BookSerializer_v2(instance=book_list)
            return Response(res.data)
    
        def put(self, request, pk):
            book_list = Book.objects.filter(pk=pk).first()
            # 既有instance,又有data,表示修改
            res = BookSerializer_v2(instance=book_list, data=request.data)
            if res.is_valid():
                res.save()
                return Response(res.data)
            return Response({"code": 1001, "msg": "数据验证失败", "error": res.errors})
    
        def delete(self, request, pk):
            res = Book.objects.filter(pk=pk).delete()
            print(res)
            return Response({"code": 1002, "msg": "数据删除成功"})
    
    

    urls.py

    rom django.contrib import admin
    from django.urls import path
    from drftest import  views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('books_v2/', views.BookView_v2.as_view()),
        path('books_v2/<int:pk>/', views.BookViewDetail_v2.as_view()),
    ]
    

    image-20220331210742393

    注意: 查询、修改和删除都没有问题,但是新增有问题,因为涉及到多表,而且Book表中的publish字段为主键和Publish表相关联。直接新增会报错,要先在Publish表中有了相关数据才能新增,同理Authors表中新增也一样,它和AuthDetail相关联。所以对应的表中都了数据才新增成功。

    1.7 编写视图函数

    之前继承APIView编写视图函数,现在可以使用GenericAPIView它继承了APIView,比之前多了一些属性和方法。

    1.7.1 编写视图函数第二种方法

    继承GenericAPIView方法写视图函数(以publish表为例,编写5个接口),其他的内容不动。

    views.py

    from django.shortcuts import render
    from drfViews.serializers import BookSerializer, BookSerializer_v2, PublishSerializer,AuthDetailSerializer,AuthorsSerializer
    from drfViews.models import Book, Publish,AuthDetail, Authors
    
    from rest_framework.views import APIView
    from rest_framework.generics import  GenericAPIView
    from rest_framework.response import  Response
    
    class PublishView(GenericAPIView):
        queryset = Publish.objects.all()   # 这个Publish表中全部数据,名字必须为queryset
        serializer_class = PublishSerializer  # 用来序列化的类, 名字必须为serializer_class
    
        def get(self,request):
            obj = self.get_queryset()  # get_queryset就是queryset
            # ser = self.serializers(instance=obj,many=True)
            ser = self.get_serializer(instance=obj, many = True) # 和上面的代码同样功能
    
            return Response(ser.data)
    
        def post(self,request):
            # ser = PublishSerializer(data=request.data)
            ser = self.get_serializer(data=request.data) # 和上面的代码同样功能
            if ser.is_valid():
                ser.save()
                return Response({"code": 2000, 'msg': '数据新增成功', 'data': ser.data})
            return Response({"code": 4000, 'msg': '数据检验失败', 'errors': ser.errors})
    
    class PublishViewDetail(GenericAPIView):
        queryset = Publish.objects.all()
        serializer_class = PublishSerializer
    
        def get(self,request, *args, **kwargs):
            # publish = Publish.objects.all().filter(pk=pk).first()
            obj = self.get_object() # 和上面的代码同样功能
    
            # ser = PublishSerializer(instance=publish)
            ser = self.get_serializer(instance=obj) # 和上面的代码同样功能
    
            return Response(ser.data)
    
    
        def put(self,request, *args, **kwargs):
            obj = self.get_object()
            ser = self.get_serializer(instance = obj, data = request.data) # # 既有instance,又有data,表示修改
            if ser.is_valid():
                ser.save()
                return Response({"code": 2001, 'msg': '数据修改成功', 'data': ser.data})
            return Response({"code": 4000, 'msg': '数据检验失败', 'errors': ser.errors})
    
    
    
        def delete(self,request, *args, **kwargs):
            obj = self.get_object().delete()
            return Response({"code": 2002, 'msg': '数据删除成功'})
    

    路由ulrs.py

    from django.contrib import admin
    from django.urls import path, include
    from drfViews import  views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('publish/',views.PublishView.as_view()),
        path('publish/<int:pk>/',views.PublishViewDetail.as_view()),
    ]
    

    GET查全部

    image-20220402000254316

    POST 新增数据

    image-20220402000417543

    GET 查一条数据

    image-20220402000449512

    PUT 修改数据

    image-20220402000754881

    DELETE 删除数据

    image-20220402000959721

    1.7.2 编写视图函数第三种方法

    使用rest_framework.mixins里面的五个扩展视图类配合GenericAPIView

    from rest_framework.mixins import
    ListModelMixin,   	# 列出所有数据集(get查所有)
    CreateModelMixin,	# 创建实例(post创建数据)
    DestroyModelMixin,  # 删除实例 (delete删除数据)
    RetrieveModelMixin,	# 检索实例 (get查一条数据)
    UpdateModelMixin	# 更新实例 (put更新数据)5个是视图扩展类(不是视图类,没有集成APIView,需要配合GenericAPIView),这五个类是单独的,它们没用继承其他类
    
    
    这五个扩展视图类在使用的时候,一定要配合GenericAPIView
    

    (以Authors表为例,编写5个接口)

    views.py

    from django.shortcuts import render
    from drfViews.serializers import BookSerializer, BookSerializer_v2, PublishSerializer,AuthDetailSerializer,AuthorsSerializer
    from drfViews.models import Book, Publish,AuthDetail, Authors
    
    from rest_framework.views import APIView
    from rest_framework.generics import  GenericAPIView
    from rest_framework.mixins import CreateModelMixin,ListModelMixin,DestroyModelMixin,RetrieveModelMixin,UpdateModelMixin
    from rest_framework.response import  Response
    
    # 取全部数据和新增,继承CreateModelMixin,ListModelMixin
    class AuthorsView(GenericAPIView,CreateModelMixin,ListModelMixin):
        queryset = Authors.objects.all()  # 拿到实例
        serializer_class = AuthorsSerializer  # 序列化类
    
        def get(self,request):
            return super().list(request)  # 取全部数据
    
        def post(self,request): 
            return super().create(request) # 取新增数据
    
    
    class AuthorsDetailView(GenericAPIView, UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin):
        queryset = Authors.objects.all()
        serializer_class = AuthorsSerializer
    
        def get(self, request, *args, **kwargs):
            return super().retrieve(request, *args, **kwargs)   # 取某指定数据(一条)
    
        def put(self, request, *args, **kwargs):
            return  super().update(request, *args, **kwargs) # 更新数据
    
        def delete(self,request, *args, **kwargs):
            return super().destroy(request, *args, **kwargs)   # 删除数据
    
    

    路由urls.py

    from django.contrib import admin
    from django.urls import path
    from drfViews import  views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('author/', views.AuthorsView.as_view()),
        path('author/<int:pk>/', views.AuthorsDetailView.as_view()),
    ]
    

    由于AuthorsAuthDetail表有外键关联,在新增作者的时候,作者详情表要先有对应的数据才能新增成功,

    所以在新增作者的时候要么先新增作者详情表,要么在models.pyAuthors里面重写create方法新增这两个表的数据。

    serializers.pyAuthorSerialzier里面重写create方法:

    serializers.py

    address = serializers.CharField(write_only=True)
    phone = serializers.CharField(max_length=20, write_only=True)
    
    def create(self,validated_data):
        datail = AuthDetail.objects.create(address=validated_data.get('address'), phone=validated_data.get('phone'))
    	print(datail)
    	author = Authors.objects.create(authdetail=datail, name=validated_data.get('name'),city=validated_data.get('city'))
    	return author
    

    1.7.3 编写视图函数第四种方法

    通过9个视图子类,编写视图函数.

    from rest_framework.generics import 
    CreateAPIView,		# 创建	(POST)
    ListAPIView, 		# 显示	(GET查全部)
    DestroyAPIView, 	# 删除	(delete)
    RetrieveAPIView, 	# 筛选	(GET查某一条)
    UpdateAPIView,		# 更新	(UPDATE)
    ListCreateAPIView, 	# 创建和显示		(POST、GET全部)
    RetrieveUpdateAPIView, 	#筛选和更新	(GET查某一条、UPDATE)
    RetrieveUpdateDestroyAPIView,	# 筛选,更新和删除	(GET查某一条、UPDATE和DELETE)
    RetrieveDestroyAPIView		# 筛选和删除		(GET查某一条和DELETE)
    
    
    继承了这些类后,里面的增删改查接口都现实了。
    

    AuthDetail表为例实现五个接口

    views.py

    from rest_framework.generics import CreateAPIView, ListAPIView,UpdateAPIView,RetrieveAPIView, DestroyAPIView
    
    class AuthDetailView(ListAPIView,CreateAPIView):
        #  查询所有和新增
        queryset = AuthDetail.objects.all()
        serializer_class = AuthDetailSerializer
    
    class AuthDetail_detailView(RetrieveAPIView,UpdateAPIView,DestroyAPIView):
        # 查询单条,删除,修改
        queryset = AuthDetail.objects.all()
        serializer_class = AuthDetailSerializer
    

    urls.py

    from django.contrib import admin
    from django.urls import path, include
    from drfViews import  views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        
        path('authDetail/', views.AuthDetailView.as_view()),
        path('authDetail/<int:pk>/', views.AuthDetail_detailView.as_view()),
    ]
    

    GET获取所有

    image-20220403144243319

    POST新增

    image-20220403144526278

    GET获取一条

    image-20220403144612163

    PUT修改

    image-20220403145352162

    DELETE删除

    image-20220403145430688

    除了这五个类之外还有四个组合的类

    from rest_framework.generics import
    ListCreateAPIView,
    RetrieveUpdateAPIView, 
    RetrieveUpdateDestroyAPIView,	
    RetrieveDestroyAPIView,
    

    使用这四个组合的类:

    from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView, RetrieveUpdateAPIView,RetrieveDestroyAPIView
    
    class AuthDetailView(ListCreateAPIView):   
        # 就相当于ListAPIView,CreateAPIView  查询所有和新增
        queryset = AuthDetail.objects.all()
        serializer_class = AuthDetailSerializer
    
        
    class AuthDetail_detailView(RetrieveUpdateDestroyAPIView): 
        # 相当于:RetrieveAPIView,UpdateAPIView,DestroyAPIView 查询单条,删除,修改
        queryset = AuthDetail.objects.all()
        serializer_class = AuthDetailSerializer
        
    #实现和上面一样的结果
    
    
    class AuthDetail_detailView(RetrieveUpdateAPIView)  # 查询单条和更新
    
    class AuthDetail_detailView(RetrieveDestroyAPIView) # 查询单条和删除
    
    class AuthDetail_detailView(UpdateAPIView,DestroyAPIView) # 更新和删除
    
    
    # 这种方法以后是用的最多的,因为可以重写一些方法:
    # 有可能要重写--》get_queryset--》get_serializer_class--》perform_create--》get,post方法
    

    1.7.4 编写视图函数第五种方法

    5个接口都使用一个视图类:ModelViewSet,但是需要修改路由。

    views.py

    from rest_framework.viewsets import ModelViewSet
    
    class AuthDetailView(ModelViewSet):   # 5个接口
        queryset = AuthDetail.objects.all()
        serializer_class = AuthDetailSerializer
    

    urls.py

    from django.contrib import admin
    from django.urls import path
    from drfViews import  views
    #导入DRF的routers模块
    from rest_framework import  routers
    
    router = routers.SimpleRouter()
    #注册
    router.register('authDetailView',views.AuthDetailView, 'authDetailView')
    
    urlpatterns = [
    	path('admin/', admin.site.urls),
    ]
    urlpatterns += router.urls
    
    

    这样五个接口就可以访问了。

    但有时候,我们只想让访问GET接口,其他的接口不能访问,就可以使用ReadOnlyModelViewSet

    """views.py"""
    from rest_framework.viewsets import  ReadOnlyModelViewSet
    
    class AuthDetailView(ReadOnlyModelViewSet):
        queryset = AuthDetail.objects.all()
        serializer_class = AuthDetailSerializer
        
        
    """urls.py"""
    
    from django.contrib import admin
    from django.urls import path
    from drfViews import  views
    #导入DRF的routers模块
    from rest_framework import  routers
    
    router = routers.SimpleRouter()
    #注册
    router.register('authDetailView',views.AuthDetailView, 'authDetailView')
    
    urlpatterns = [
    	path('admin/', admin.site.urls),
    ]
    urlpatterns += router.urls
    
    使用GET方法访问时是没有问题的,但使用POST,PUT,DELETE方法则会报错
        
    "detail": "Method \"POST\" not allowed."
    "detail": "Method \"PUT\" not allowed."
    "detail": "Method \"DELETE\" not allowed."
    

    ModelViewSetReadOnlyModelViewSet之所有能达到这种效果,主要是它们继承了以下几个类。

    from rest_framework.viewsets import 
    
    class ModelViewSet(mixins.CreateModelMixin,
                       mixins.RetrieveModelMixin,
                       mixins.UpdateModelMixin,
                       mixins.DestroyModelMixin,
                       mixins.ListModelMixin,
                       GenericViewSet):
        pass	
        
    class ReadOnlyModelViewSet(mixins.RetrieveModelMixin,
                               mixins.ListModelMixin,
                               GenericViewSet):
            pass	
        
    """    
    CreateModelMixin,
    RetrieveModelMixin,
    UpdateModelMixin,
    DestroyModelMixin,
    ListModelMixin, 
    """ # 这五个就是上面写的增删改查的 扩展视图类
    
    # 而且这两个类都继承了GenericViewSet,
    class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
        pass
    
    # 之所有要重写路由主要是:ViewSetMixin类,使用它就必须要重写路由。
    

    以后只要想重写路由必须要继承ViewSetMixin类或子类

    from rest_framework.viewsets import 
    ViewSetMixin
    
    ModelViewSet = CreateModelMixin+RetrieveModelMixin+UpdateModelMixin+DestroyModelMixin+ListModelMixin+GenericViewSet
    
    ReadOnlyModelViewSet = RetrieveModelMixin+ListModelMixin+GenericViewSet
    
    GenericViewSet = ViewSetMixin+generics.GenericAPIView
    
    ViewSet = ViewSetMixin +  views.APIView
    
    # 只要继承了ViewSetMixin以子类ModelViewSet,ReadOnlyModelViewSet,GenericViewSet,ViewSet就必须在urls.py里导入routes自动生成路由
    

    1.7.5 以上类的总结:

    image-20220404000545183

    2. 路由组件

    2.1 两种使用ViewSetMixin的方法

    # 方法一:
    from rest_framework.views import APIView
    from rest_framework.viewsets import ViewSetMixin
    
    class TestAPIView(ViewSetMixin,APIView):  # ViewSetMixin必须要写在前面
        pass
    
    from rest_framework.viewsets import ViewSet
    class TestAPIView(ViewSet):
        pass
    
    
    TestAPIView(ViewSet)  == TestAPIView(ViewSetMixin,APIView):
        
        
    # 方法二:
    from rest_framework.generics import GenericAPIView
    from rest_framework.viewsets import ViewSetMixin
    
    class TestGenericAPIView(ViewSetMixin,GenericAPIView):  # ViewSetMixin必须要写在前面
        pass
    
    
    from rest_framework.viewsets import GenericViewSet
    class TestGenericAPIView(GenericViewSet):  
        pass
    
    TestGenericAPIView(GenericViewSet)  == TestGenericAPIView(ViewSetMixin,GenericAPIView):
    
    

    只要继承了ViewSetMixin视图类中的方法就可以不用写成之前的get,post,put,delete,名字随意写,不过要在路由中写成path('url/',views.类名.as_view({'get':'自己定义的方法','put':'自己定义的方法2'}))

    示例:

    """views.py"""
    from rest_framework.viewsets import ViewSet
    class TestView(ViewSet):
        def list(self,request):
            return Response("GET方法")
        def create(self,request):
            return Response("POST方法")
        
        
    """urls.py"""
    
    from django.contrib import admin
    from django.urls import path
    from drfViews import  views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('testView/', views.TestView.as_view({'get':"list",'post':"create"}))
    
    ]
    

    image-20220404001923398

    image-20220404001940128

    实例这种写法过程:

    视图中的类中继承了ViewSetMixin类,而ViewSetMixin方法中重新写了as_view()方法,而重写的as_veiw()方法用法:
        view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
        
    def as_view(cls, actions=None, **initkwargs):
    其中actions就是{'get': 'list', 'post': 'create'},如果actions没有值则会报错:
        "The `actions` argument must be provided when "
        	"calling `.as_view()` on a ViewSet. For example "
        	"`.as_view({'get': 'list'})`"
           
    

    2.2 自动生成路由步骤:

    自动生成路由必须要继承的类或子类:

    GenericViewSet + 5个扩展视图类之一(
        				CreateModelMixin,
    					RetrieveModelMixin,
    					UpdateModelMixin,
    					DestroyModelMixin,
    					ListModelMixin, 
    					)
    才能自动生成,因为请求要相互对应:
    {'get':'list','post':'create','put':'update','delete':'destroy','get':'retrieve'}
    
    # 这就是为什么ModelViewSet和 ReadOnlyModelViewSet可以自动生成路由,如果没有继承这5个扩展视图类之一,则不能自动生成
    

    自动生成路由步骤:

    # 第一步 导入routers
    from django.urls import path, include
    from rest_framework import  routers
    # 第二步 实例化:
    router = routers.SimpleRouter()
    
    # 第三步 注册:
    router.register('URL地址',views.对应的类名, '别名')
    router.register('authDetailView',views.AuthDetailView, 'authDetailView')
    # 第四步:
    urlpatterns += router.urls
    或
    urlpatterns = [
        path('URL地址', include(router.urls) )
    ]
    
    

    2.3 action再生成路由

    from rest_framework.viewsets import GenericViewSet
    
    from rest_framework.decorators import action
    
    class TestView(GenericViewSet):
        @action(methods=['GET', 'POST'], detail=False)
        def login(self,request):
            return Response("GET方法")
        def test(self,request):
            return Response("POST方法")
    
    
    
        
    action(methods=None, detail=None, url_path=None, url_name=None, **kwargs):
        methods = 请求方式,是个列表
        detail = 是否带id, False为不带
        url_path= url地址,不写则默认方法名为地址
        url_name = 别名
        
        
    给上面的login加上action装饰器后,
    会再之前http://172.0.0.1:8000/test/的后面生成路径--->:http://172.0.0.1:8000/test/login/
    访问的时候直接访问http://172.0.0.1:8000/test/login/,由于方法里methods=['GET', 'POST']写了get和post方法,所以get和post方法都会执行。
        如果设置url_path
        @action(methods=['GET', 'POST'], detail=False,url_path='hello')
        def login(self,request):
        则访问的时候就要访问:
        http://172.0.0.1:8000/test/hello/
                
    
                
    如果 detail=True(方法不常用), 则访问路径: http://172.0.0.1:8000/test/pk值/login/
            
    代码:  
    """   
    from rest_framework.viewsets import GenericViewSet
    from rest_framework.decorators import action
    
    class TestView(GenericViewSet):
        @action(methods=['GET', 'POST'], detail=True)
        def login(self,request,pk):
            return Response("GET方法")
        def test(self,request):
            return Response("POST方法")
    """
    
    
    
    路由:urls.py
    """
    from django.contrib import admin
    from django.urls import path, include
    from drfViews import  views
    from rest_framework import  routers
    
    router = routers.DefaultRouter()
    router.register('test',views.TestView,'test')
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('api/',include(router.urls))
    ]
    """
    
    上面把自动成生的路由放在了api里面所以访问地址:
        http://127.0.0.1:8000/api/test/login/
    
  • 相关阅读:
    QT中,QTableWidget 使用示例详细说明
    淘宝API接口,交易,退款退货,物流数据获取,erp系统对接交易订单
    CSS学习小结
    零基础学Java(10)面向对象-使用LocalDate类完成日历设计
    Java项目:JSP酒店客房管理系统
    闭包(C#)
    MQ中的坑及高并发下保证接口的幂等性
    【云原生 | 27】Docker部署运行开源消息队列实现RabbitMQ
    c#设计模式-行为型模式 之 访问者模式
    mysql做查询时,第一次很慢,第二三次就会很快?
  • 原文地址:https://www.cnblogs.com/hans-python/p/16251232.html