DRF:序列化器、视图、路由

 

from django.db import models

# Create your models here.

__all__ = ["Book", "Publisher", "Author"]


class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name="图书名称")
    CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux"))
    category = models.IntegerField(choices=CHOICES, verbose_name="图书的类别")
    # pub_time = models.DateField(verbose_name="图书的出版日期")

    publisher = models.ForeignKey(to="Publisher", on_delete=None)
    author = models.ManyToManyField(to="Author")

    def __str__(self):
        return self.title

    class Meta:
        verbose_name_plural = "01-图书表"
        db_table = verbose_name_plural


class Publisher(models.Model):
    title = models.CharField(max_length=32, verbose_name="出版社的名称")

    def __str__(self):
        return self.title

    class Meta:
        verbose_name_plural = "02-出版社表"
        db_table = verbose_name_plural


class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name="作者的姓名")

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = "03-作者表"
        db_table = verbose_name_plural
model

 

          Serializer

 

序列化

1、声明序列化类

from rest_framework import serializers

# 定义序列化类,写需要序列化的字段
class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    title = serializers.CharField(max_length=32) 
    CHOICES = ((1, 'Python'), (2, 'Linux'), (3, 'Go')) 
    
    # 对于ChoiceField:source="get_category_display
    # 如过没有指定source,拿到的依然是数字,get_字段名_display    
    category = serializers.ChoiceField(choices=CHOICES, source='get_category_display')
View Code

2、视图应用

from rest_framework.views import APIView
from rest_framework.response import Response

class BookView(APIView):
    def get(self, request):
        book_list = Book.objects.all()
        # 对于多条数据,many=True
        ret = BookSerializer(book_list, many=True)
        return Response(ret.data)
View Code

3、外键关系的序列化

    a、声明外键对象的序列化

from rest_framework import serializers


class AuthorSerializers(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField(max_length=32)


class PublisherSerializers(serializers.Serializer):
    id = serializers.IntegerField()
    title = serializers.CharField(max_length=32)
View Code

    b、对于M2M需要指定 many=True

    class BookSerializer(serializers.Serializer):
        id = serializers.IntegerField(required=False)
        CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux"))
        category = serializers.ChoiceField(choices=CHOICES, source="get_category_display")

        publisher = PublisherSerializer()
        author = AuthorSerializer(many=True)
View Code

 

 

反序列化

  新增数据--post请求

  同样,将前端返回的数据交给序列化器,然后序列化器会自动验证,与Form组件验证类似。 

  在反序列化时,会出现数据校验规则与序列化不同的情况,此时可以read_only和write_only设置序列化和反序列验证的字段及规则。对于post请求,必须重写create方法。

class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(max_length=32)
    CHOICES = ((1, "Linux"), (2, "Django"), (3, "Python"))
    chapter = serializers.ChoiceField(choices=CHOICES, source="get_chapter_display", read_only=True)
    w_chapter = serializers.IntegerField(write_only=True)
    pub_time = serializers.DateField()

    publisher = PublisherSerializer(read_only=True)
    user = UserSerializer(many=True, read_only=True)

    users = serializers.ListField(write_only=True)
    publisher_id = serializers.IntegerField(write_only=True)

    def create(self, validated_data):
        # post请求必须重写该方法
        book = Book.objects.create(title=validated_data["title"], chapter=validated_data["w_chapter"], pub_time=validated_data["pub_time"],                                  publisher_id=validated_data["publisher_id"])
        book.user.add(*validated_data["users"])
        return book
序列化器
class BookView(APIView):

    def get(self, request):
        # BookSerializer序列化哪些字段,展示哪些字段
        # book = Book.objects.first()
        # ret = BookSerializer(book)

        books = Book.objects.all()
        ret = BookSerializer(books, many=True)
        return Response(ret.data)

    def post(self, request):
        serializer = BookSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        else:
            return Response(serializer.errors)
View Code

 

    def post(self, request):
        serializer = OrganizationSerializer(data=request.data)
        # print(serializer.data)
        # AssertionError: When a serializer is passed a `data` keyword argument you must call `.is_valid()` 
        # before attempting to access the serialized `.data` representation.
        # You should either call `.is_valid()` first, or access `.initial_data` instead.
        if serializer.is_valid():
            # print(serializer.data)
            # AssertionError: You cannot call `.save()` after accessing `serializer.data`.
            # If you need to access data before committing to the database then
            # inspect 'serializer.validated_data' instead.
            serializer.save()
            return Response(serializer.data)
随意调用request.data的坑

 

 修改数据--put请求

查看单条数据时,即查看某个数据的详细信息时,对具体某条数据的操作携带ID。

path('retrieve/<int:id>', BookEditView.as_view())
class BookEditView(APIView):

    def get(self, request, id):
        book_obj = Book.objects.filter(id=id).first()
        ret = BookSerializer(book_obj)
        return Response(ret.data)

    def put(self, request, id):
        book_obj = Book.objects.filter(id=id).first()
        # partial=True, 允许部分数据更新
        serializer = BookSerializer(book_obj, data=request.data, partial=True)

        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        else:
            return Response(serializer.errors)
views
class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)
    title = serializers.CharField(max_length=32, validators=[my_validate])
    CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux"))
    category = serializers.ChoiceField(choices=CHOICES, source="get_category_display", read_only=True)
    w_category = serializers.ChoiceField(choices=CHOICES, write_only=True)
    pub_time = serializers.DateField()

    publisher = PublisherSerializer(read_only=True)
    publisher_id = serializers.IntegerField(write_only=True)
    author = AuthorSerializer(many=True, read_only=True)
    author_list = serializers.ListField(write_only=True)

    def create(self, validated_data):
        book = Book.objects.create(title=validated_data["title"], category=validated_data["w_category"],
                                pub_time=validated_data["pub_time"], publisher_id=validated_data["publisher_id"])
        book.author.add(*validated_data["author_list"])
        return book

    def update(self, instance, validated_data):
        # instance即视图put方法中序列化器接受的第一个参数bookobj
        instance.title = validated_data.get("title", instance.title)
        instance.category = validated_data.get("category", instance.category)
        instance.pub_time = validated_data.get("pub_time", instance.pub_time)
        instance.publisher_id = validated_data.get("publisher_id", instance.publisher_id)
        if validated_data.get("author_list"):
            instance.author.set(validated_data["author_list"])
        instance.save()
        return instance
序列化器

******总结******

get:把model对象或querySet对象交给序列化器

post:把前端传的数据(QueryDict)交给序列化器, 交给序列化器的数据最终交给序列化器的create方法,告诉序列化器插入哪些数据

put:把model对象和前端传的数据(QueryDict)交给序列化器,交给序列化器的数据最终交给序列化器的update方法,在update方法中更新model对象的数据,注意M2M字段的更新。

 

验证

如果验证失败,抛异常serializers.ValidationError()

class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(max_length=32)
    
    
    # 单字段验证钩子:validate_验证的字段
    def validate_title(self, value):
        if "python" not in value.lower():
            raise serializers.ValidationError("标题必须含有python")
        return value

    # 全局验证钩子
    def validate(self, attrs):
        # attrs:是前端返回的数据,可以理解为QueryDict
        if attrs["w_category"] == 1 and attrs["publisher_id"] == 1:
            return attrs
        else:
            raise serializers.ValidationError("分类以及标题不符合要求")
钩子函数

在序列化器定义字段时,可以给validators参数传递一个列表,列表内为自定义的验证规则

def my_validate(value):
    if "敏感信息" in value.lower():
        raise serializers.ValidationError("不能含有敏感信息")
    else:
        return value
自定义验证规则
class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(max_length=32, validators=[my_validate])
使用

 

              ModelSerializer

序列化

class BookSerializer(serializers.ModelSerializer):
    # 显示choicefield中文
    category = serializers.ChoiceField(source="get_category_display")
    
    class Meta:
        model = Book
        # fields指定序列化哪些字段
        # fields = ["id", "title", "pub_time"]
        fields = "__all__"
        # depth表示序列化的深度,对于外键
        depth = 1
ModelSerializer基本使用

对于FK可能有很多数据,如果简单粗暴的使用depth,会全部拿到,可能存在很多我们不需要的字段。

class BookSerializer(serializers.ModelSerializer):
    # 第一步:实例化SerializerMethodField对象,赋值给变量
    publisher_info = serializers.SerializerMethodField()
    authors = serializers.SerializerMethodField()

    def get_authors(self, obj):
        authors_query_set = obj.author.all()
        return [{"id": author_obj.id, "name": author_obj.name} for author_obj in authors_query_set]
    
    # 第二步:定义序列化规则:get_实例名
    def get_publisher_info(self, obj):
        # obj 是我们序列化的每个Book对象
        publisher_obj = obj.publisher  # Publisher对象
        return {"id": publisher_obj.id, "title": publisher_obj.title}

    class Meta:
        model = Book
        fields = "__all__"
SerializerMethodField自定义外键验证规则

对于生成的每个SerializerMethodField实例,写相应的方法去处理,验证规则由get_***方法决定。

反序列化

ModelSerializer已经帮我们实现了post、put请求需要的create、update方法
在使用SerializerMethodField时,定义的变量名不要与model里的字段重名,
我们自定义的SerializerMethodField是用来展示的,定义read_only=True
对于ModelSerializer自行定义的,设置--字段名: {"write_only": True}, 这里的字段名必须与model里相同,配置在extra_kwargs
class BookSerializer(serializers.ModelSerializer):
    category_display = serializers.SerializerMethodField(read_only=True)
    publisher_info = serializers.SerializerMethodField(read_only=True)
    authors = serializers.SerializerMethodField(read_only=True)

    # SerializerMethodField不仅可以定义外键,对ChoiceField也试用
    def get_category_display(self, obj):
        return obj.get_category_display()

    def get_authors(self, obj):
        authors_query_set = obj.author.all()
        return [{"id": author_obj.id, "name": author_obj.name} for author_obj in authors_query_set]

    def get_publisher_info(self, obj):
        # obj 是我们序列化的每个Book对象
        publisher_obj = obj.publisher  # Publisher对象
        return {"id": publisher_obj.id, "title": publisher_obj.title}

    class Meta:
        model = Book
        fields = "__all__"

        # 配置元信息
        extra_kwargs = {
            "category": {"write_only": True}, 
            "publisher": {"write_only": True},
            "author": {"write_only": True}
        }
View Code

Meta中其它关键字参数

class BookSerializer(serializers.ModelSerializer):
    chapter = serializers.CharField(source="get_chapter_display", read_only=True)
    
    
    class Meta:
        model = Book
        # fields = "__all__"  # 所有字段
        
        # 包含某些字段, 字段是有序的
        fields = ["id", "title","dis_chapter", "pub_time", "publishers", "users","chapter", "user", "publisher"]
        # exclude = ["user"]  # 排除某些字段
        
        # 只读字段
        read_only_fields = ["id", "dis_chapter", "users", "publishers"]
        
        # 对字段的其他设置
        extra_kwargs = {"title": {"validators": [my_validate,]}, "user": {"write_only": True}, "publisher": {"write_only": True},
                        "chapter": {"write_only": True}}
Meta

 

 

 

对 APIView的封装

 第一层封装:

  GenericAPIView,封装ORM查询和序列化器

  Mixin,封装get、post、put、delete等方法

class GenericAPIView(APIView):
    queryset = None
    serializer_class = None

    def get_queryset(self):
        return self.queryset.all()

    def get_serializer(self, *args, **kwargs):
        return self.serializer_class(*args, **kwargs)


class ListModelMixin(object):
    def list(self, request, *args, **kwargs):
        queryset = self.get_queryset()
        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)


class CreateModelMixin(object):
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.validated_data)
        else:
            return Response(serializer.errors)


class RetrieveModelMixin(object):
    def retrieve(self, request, id, *args, **kwargs):
        book_obj = self.get_queryset().filter(pk=id).first()
        book_ser = self.get_serializer(book_obj)
        return Response(book_ser.data)


class UpdateModelMixin(object):
    def update(self, request, id, *args, **kwargs):
        book_obj = self.get_queryset().filter(pk=id).first()
        book_ser = self.get_serializer(book_obj, data=request.data, partial=True)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.validated_data)
        else:
            return Response(book_ser.errors)


class DestroyModelMixin(object):
    def destroy(self, request, id, *args, **kwargs):
        queryset = self.get_queryset()
        try:
            queryset.get(pk=id).delete()
            return Response("")
        except Exception as e:
            return Response("信息有误")
# 我们把公共的部分抽出来 这样不管写多少表的增删改查都变的很简单
# 这样封装后我们的视图会变成这样

class BookView(GenericAPIView, ListModelMixin, CreateModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)


class BookEditView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request, id, *args, **kwargs):
        return self.retrieve(request, id, *args, **kwargs)

    def patch(self, request, id, *args, **kwargs):
        return self.update(request, id, *args, **kwargs)

    def destroy(self, request, id, *args, **kwargs):
        return self.delete(request, id, *args, **kwargs)
GenericAPIView和各种Mixin

 第二层封装:

   用一个类去专门封装第一层封装的各种类。

  但是还存在一个问题,对于不需要传id的get所有和post请求,需要传id的get单条、修改put、删除delete,需要两个url,两个视图,对这两个再做一次封装的话,需要给as_view()传参。

 

 第三层

from rest_framework.viewsets import ViewSetMixin, 重写as_view(),在url上携带参数,url上的id只能用pk。
在执行dispatch之前,对匹配规则做一些修改。
让get请求去找list方法,post请求找create方法,等等,在视图中甚至不用再实现get、post、put,而只需要匹配QuerySet和序列化器。

 

class BookModelViewSet(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
视图
urlpatterns = [
    # path('list', BookView.as_view()),
    # path('retrieve/<int:id>', BookEditView.as_view()),
    path('list', BookModelViewSet.as_view({"get": "list", "post": "create"})),
    path('retrieve/<int:pk>', BookModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
]
urls

 

class ModelViewSet(mixins.CreateModelMixin,  # create  新增
                   mixins.RetrieveModelMixin,  # retrieve  get单条
                   mixins.UpdateModelMixin,  # update  修改
                   mixins.DestroyModelMixin,  # destroy  删除
                   mixins.ListModelMixin,  # list  get all
                   
                   GenericViewSet):
    """
    A viewset that provides default `create()`, `retrieve()`, `update()`,
    `partial_update()`, `destroy()` and `list()` actions.
    """
    pass


# ViewSetMixin:重写as_view,使路由可以传参,在dispatch之前修改路由匹配规则
# GenericAPIView(views.APIView)
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
    """
    The GenericViewSet class does not provide any actions by default,
    but does include the base set of generic view behavior, such as
    the `get_object` and `get_queryset` methods.
    """
    pass
ModelViewSet

 

视图依赖:

from rest_framework import views
from rest_framework import generics
from rest_framework import mixins
from rest_framework import viewsets

 

 自动生成url

按之前的方法,如果要实现增删改查,需要为一个视图写两个url,DRF的routers可以帮我们自动去生成路由。

如果用这种方法,则会为一个视图生成五个url,跟据实际需要确定是否这么玩儿。

from django.urls import path, include
from rest_framework import routers  # 1
from app01 import views

routers = routers.DefaultRouter()  # 2
routers.register("authors", views.AuthorViewSet)  # 3
routers.register("books", views.BookViewSet)  # 3

urlpatterns = [
    path('', include(routers.urls))  # 4
]
方式一
from .views import BookModelViewSet
from rest_framework.routers import DefaultRouter  # 1
 
router = DefaultRouter()  # 2

router.register(r"$", BookModelViewSet)  # 3


urlpatterns = [

]


urlpatterns += router.urls  # 4
方式二

 

 

 

posted @ 2018-11-08 13:55  web123  阅读(191)  评论(0编辑  收藏  举报