3

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

 1 year ago
source link: https://www.cnblogs.com/hans-python/p/16251232.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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.

"""

方法二:在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)
    ...
    
# 其他的内容不变

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

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)
"""

书籍名字为不符合规范

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

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  # 没有问题,正常返回

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

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

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()),
]

访问新接口:

修改一条数据

修改价格为:12.8

使用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()),
]

注意: 查询、修改和删除都没有问题,但是新增有问题,因为涉及到多表,而且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查全部

POST 新增数据

GET 查一条数据

PUT 修改数据

DELETE 删除数据

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获取所有

POST新增

GET获取一条

PUT修改

DELETE删除

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

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 以上类的总结:

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/

Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK