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
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')
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)
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)
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)
反序列化
新增数据--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)
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)
修改数据--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)
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
对于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实例,写相应的方法去处理,验证规则由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} }
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}}
对 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)
第二层封装:
用一个类去专门封装第一层封装的各种类。
但是还存在一个问题,对于不需要传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"})), ]
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
视图依赖:
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