restful协议中,一切皆是资源,操作只是请求方式
model_to_dict()方法:
from django.forms.models import model_to_dict obj = Publish.objects.filter(pk=1).first() model_to_dict(obj) # model_to_dict(对象)是Django的一个方法:返回一个字典,key是obj 这个对象的字段名,value是字段对应的值
restframework 下的 APIView:
# APIView流程: 1.re_path(r"^book/$",views.BookView.as_view(),name="books"), # 最终执行的是View下的view 2."book/"一旦被访问,view(request)执行 # 此时是旧的 request(原生的) <=======>等同于 view(request)执行的是 APIView这个类下的 dispatch() <=====> 请求方式对应的实例方法 3.dispatch():
# 包含初始化操作 initial() (初始化后的request都是新的request) 构建新的request对象: self.request = Request(request) self.request._request # 可得到旧的request self.request.GET # 获取GET请求数据 self.request.data # 获取POST、PUT请求数据 (不用 self.request.POST的原因:.POST只能获取到 urlencoded 这个类型的请求数据) 4. 做分发: 如 GET请求:--- self.get(self,request)方法 # 此时是新的request # 原生的request(Django提供)支持的操作: request.body # 请求体里面的原生数据 request.POST ... from rest_framework.views import APIView # 新的request()支持的操作: request._request # Django提供的原生的request,可 request._request.POST 等操作 request.data # 所有的请求数据(POST请求);已经反序列化后的结果 request.GET # 新的request也有 .GET的方法,相当于 request._request.GET
serializers.serialize(数据类型,QuerySet):将QuerySet序列化(Django方法)
# serializers是Django提供的一个方法,不属于 restful from django.core import serializers publish_list = Publish.objects.all() ret = serializers.serialize("json",publish_list) # 将 publish_list这个queryset 序列化为 json格式的数据模型
serializers.Serializer (instance=,many=): 为QuerySet和model对象做序列化(REST方法)
# 这是restframework 提供的一个方法: from rest_framework import serializers book_list = Book.objects.all() class BookSerializers(serializers.Serializer): # 先创建一个序列化的类;语法 类似于 Form title = serializers.CharField() price = serializers.IntegerField() # 列出需要序列化出来的字段 publish = serializers.CharField(source="publish.name") # source="publish.name" 表示 取的是 publish表中的name字段 #(publish是一对多字段) # authors = serializers.CharField(source="authors.all") # (authors是多对多字段) # 多对多时, source 可以 .all(一个方法) authors = serializers.SerializerMethodField() # serializers.SerializerMethodField() 专门用于 多对多 字段;下面要有 get_authors方法 def get_authors(self,obj): temp = [] for i in obj.authors.all(): temp.append(i.name) return temp # 用上面创建出来的类对 publish_list 这个QuerySet 进行实例化 bs = BookhSerializers(publish_list,many=True) # many=True 表示序列化的是一个QuerySet;其还可以序列化 model对象 bs.data # 获取序列化后的一个列表套字典的形式(QuerySet;model对象就返回一个字典) """ BookhSerializers(instance=publish_list,many=True)序列化的实现原理: temp = [] for obj in publish_list: temp.append( { "title":obj.title, "price": obj.price, "publish": obj.publish.name # 有 source="publish.name" 时的情况 # 没有source="publish.name"时,是:"publish":obj.publish, (即会显示关联表中的 __str__方法) # "authors": obj.authors.all() # 有 source="authors.all"时,返回的是一个 QuerySet "authors": get_authors(obj) # SerializerMethodField() 情况下,"authors"字段对应的值 只取决于 get_authors方法的返回值 } ) 如果 obj中有 关联字段,则 "关联字段": str(obj.关联字段) (即 显示关联表中的 __str__方法)
"""
注:django_restframework 的 Serializer 和 ModelSerializer 返回给前端数据时,会在 ImageField 和 FileField 字段的路径前面自动加上 "/media/"(根据settings中的media配置),但 django的 Serializer 返回给前端数据时,不会在 ImageField 和 FileField 的路径前端加上 "/media/",这也是 django_restframework Serializer和ModelSerializer的完善之处。
REST 的 Serializer 需要自己写 create() 和 update() 方法。
serializers.ModelSerializer: # 语法类似于ModelForm # 可以直接对接数据库
class BookModelSerializer(serializers.ModelSerializer): class Meta: model = Book fields = "__all__" # 在mixins.CreateModelMixin中,ModelSerializer校验完成后,会拿着 fields 中的字段做序列化(序列化的目的是返回给前端);如果 fields中的某个字段不需要序列化(如 该字段也已经被你删除了),则可以把该字段设置为 write_only=True # 多对多字段序列化时,默认得到的是一个列表,列表中是 所有对象的pk值 # 自定制关联字段的序列化
# one2one/fk/choice字段: publish = serializers.CharField(source="publish.name")
# m2m字段: authors = serializers.SerializerMethodField() # 自定义字段名要用在下面的 get_自定义字段() 这个方面中 def get_authors(self,obj): # get_m2m自定义字段() temp = [] for i in obj.authors.all(): temp.append(i.name) return temp """ 如果没有自定制,默认用 ModelSerializer 自己的;
如果想 跨表 获取 choices 字段(假如为:city)的中文信息,可利用自定义字段的方式如: city = serializers.CharField(source="publish.get_city_display") # source 中的 publish.get_city_display 后面不用加 ();
非跨表的 choices 字段, 直接是 source="get_xx_display" """ """ restframework下的序列类 ModelSerializer: 1.将QuerySet或者 一个model对象序列化成Json数据 2.做数据校验 3.json数据 转成 QuerySet/model对象,如: def post(self,request): bs = BookModelSerializer(data=request.data) if bs.is_valid(): # 做校验 bs.save() # .save() 就是 create()方法 # 此时 json数据---> queryset/model对象 ---> 数据库中的一条记录 return Response(bs.data) """
mixins.CreateModelMixin代码:
Response() 中的 serializer.data 会按照 fields 中的字段序列化
补充:
request.query_params # 获取到 GET请求数据;request是后来封装好的request
ModelSerializer.initial_data # Django DRF 的 Serializer 和 ModelSerializer 有一个属性 initial_data,前端传过来的数据都放在 initial_data 里面(字典的形式);调用 validate() 之前的数据(字段校验之前的数据)
# 在 view 中,可以去 self 中取 request,如:self.request;但在 Serializer 中不能从 self 中取 request,而是要这样取 request : self.context["request"]
序列化示例
开发我们的Web API的第一件事是为我们的Web API提供一种将代码片段实例序列化和反序列化为诸如json
之类的表示形式的方式。我们可以通过声明与Django forms非常相似的序列化器(serializers)来实现。
models部分:
from django.db import models # Create your models here. class Book(models.Model): title=models.CharField(max_length=32) price=models.IntegerField() pub_date=models.DateField() publish=models.ForeignKey("Publish") authors=models.ManyToManyField("Author") def __str__(self): return self.title class Publish(models.Model): name=models.CharField(max_length=32) email=models.EmailField() def __str__(self): return self.name class Author(models.Model): name=models.CharField(max_length=32) age=models.IntegerField() def __str__(self): return self.name
views部分:
from rest_framework.views import APIView from rest_framework.response import Response from .models import * from django.shortcuts import HttpResponse from django.core import serializers # django的 Serializer from rest_framework import serializers # rest_framework 的 Serializer class BookSerializers(serializers.Serializer): # 该示例中 django 和 rest_framework 的 Serializer同时用这一种形式 title=serializers.CharField(max_length=32) price=serializers.IntegerField() pub_date=serializers.DateField() publish=serializers.CharField(source="publish.name") #authors=serializers.CharField(source="authors.all") authors=serializers.SerializerMethodField() def get_authors(self,obj): temp=[] for author in obj.authors.all(): temp.append(author.name) return temp class BookViewSet(APIView): def get(self,request,*args,**kwargs): book_list=Book.objects.all() # 序列化方式1: # from django.forms.models import model_to_dict # import json # data=[] # for obj in book_list: # data.append(model_to_dict(obj)) # print(data) # return HttpResponse("ok") # 序列化方式2: # data=serializers.serialize("json",book_list) # return HttpResponse(data) # 序列化方式3: bs=BookSerializers(instance=book_list,many=True) return Response(bs.data) # 序列化后都要把 bs.data 的数据返回
ModelSerializer
class BookSerializers(serializers.ModelSerializer): class Meta: model=Book fields="__all__"
提交的POST请求
def post(self,request,*args,**kwargs): bs=BookSerializers(data=request.data,many=False) if bs.is_valid(): # 做校验 # print(bs.validated_data) bs.save() # 把数据保存到数据库中(会有反序列化操作) # .save()中有 create()方法,即 save()会调用 create() 方法 return Response(bs.data) else: return HttpResponse(bs.errors) # bs.errors 表示 错误信息
重写save()中的create()方法
class BookSerializers(serializers.ModelSerializer): class Meta: model=Book fields="__all__" # exclude = ['authors',] # depth=1 # 添加多对多字段时需要重写 save()中的 create()方法 def create(self, validated_data): authors = validated_data.pop('authors') # 返回 authors(多对多字段) 并返回 obj = Book.objects.create(**validated_data) # 添加非多对多字段 obj.authors.add(*authors) # 在第三张表中添加authors return obj
单条数据的GET和PUT请求
class BookDetailViewSet(APIView): def get(self,request,pk): book_obj=Book.objects.filter(pk=pk).first() bs=BookSerializers(book_obj) # 把查到的 记录obj 序列化后返回给用户 return Response(bs.data) def put(self,request,pk): book_obj=Book.objects.filter(pk=pk).first() bs=BookSerializers(book_obj,data=request.data) # Serializer(要修改的记录对象,data=需要更新的数据) # 更新操作 if bs.is_valid(): bs.save() return Response(bs.data) else: return HttpResponse(bs.errors)
超链接API:Hyperlinked
class BookSerializers(serializers.ModelSerializer): class Meta: model=Book fields="__all__" # 让 publish 字段获取到的值为其对应的一个 url publish= serializers.HyperlinkedIdentityField( view_name='publish_detail', # 别名是“publish_detail”的url lookup_field="publish_id", # 需要查询的字段 lookup_url_kwarg="pk") # url中为“pk”的有名分组
# 有 HyperlinkedIdentityField 时, 需要在 序列化类实例化时 添加 "context={'request',request}"
urls部分:
1 urlpatterns = [ 2 url(r'^books/$', views.BookViewSet.as_view(),name="book_list"), 3 url(r'^books/(?P<pk>d+)$', views.BookDetailViewSet.as_view(),name="book_detail"), 4 url(r'^publishers/$', views.PublishViewSet.as_view(),name="publish_list"), 5 url(r'^publishers/(?P<pk>d+)$', views.PublishDetailViewSet.as_view(),name="publish_detail"), 6 ]
rest_framework 的 Serializer 和 ModelSerialzer 可参考: https://blog.csdn.net/FightFightFight/article/details/80059024
HiddenField字段:
class UserFavSerializer(serializers.ModelSerializer): user = serializers.HiddenField( default=serializers.CurrentUserDefault() ) # HiddenField 的值不需要用户自己post数据过来,也不会显示返回给用户;配合 CurrentUserDefault() 可以实现自动获取到当前用户 class Meta: model = UserFav fields = ("user","goods","id") # 如果此处还需要一个删除功能,则 fields 里面还需要加上 "id",从而创建之后也把 id 返回给前端
DRF Serializer 详细用法可参考:https://blog.csdn.net/l_vip/article/details/79156113
DRF Serializer 的 验证
1.字段级别的验证
你可以通过向你的Serializer子类中添加 .validate_<field_name> 方法来指定自定义字段级别的验证。这些类似于Django表单中的.clean_<field_name>方法。
这些方法采用单个参数,即需要验证的字段值。
你的validate_<field_name>方法应该返回 一个验证过的数据 或者 抛出一个serializers.ValidationError异常。例如:
from rest_framework import serializers class BlogPostSerializer(serializers.Serializer): title = serializers.CharField(max_length=100) content = serializers.CharField() def validate_title(self, value): """ Check that the blog post is about Django. """ if 'django' not in value.lower(): raise serializers.ValidationError("Blog post is not about Django") return value
注意: 如果你在序列化器中声明<field_name>的时候带有required=False参数,字段不被包含的时候这个验证步骤就不会执行。
2. 对象级别的验证
要执行需要访问多个字段的任何其他验证,请添加一个 .validate() 方法到你的Serializer子类中。这个方法采用字段值字典的单个参数,如果需要应该抛出一个 ValidationError异常,或者知识返回经过验证的值。例如
from rest_framework import serializers class EventSerializer(serializers.Serializer): description = serializers.CharField(max_length=100) start = serializers.DateTimeField() finish = serializers.DateTimeField() def validate(self, data): """ Check that the start is before the stop. """ if data['start'] > data['finish']: raise serializers.ValidationError("finish must occur after start") return data
DRF ModelSerializer 保存密码的方法:
# ModelSerializer 在 保存前端传过来的数据时(.save()方法),是直接把这些数据保存到数据库的;这时保存到数据库的密码也是明文的,为了将密码保存为密文的,则需要重构 .save()方法 调用的 create() 方法 from rest_framework import serializers from rest_framework.validators import UniqueValidator # 验证字段的值唯一 class UserRegSerializer(serializers.ModelSerializer): """用户注册""" code = serializers.CharField(required=True, write_only=True, max_length=4, min_length=4, label="验证码", error_messages={ "blank": "请输入验证码", "max_length": "验证码格式错误", "min_length": "验证码格式错误" }, help_text="验证码") # write_only=True 表示 只写模式,即只能反序列化,不能序列化、发送给前端 username = serializers.CharField(label="用户名",required=True,allow_blank=False,help_text="用户名", validators=[UniqueValidator(queryset=UserInfo.objects.all(),message="用户已经存在")]) # validators 表示验证器;UniqueValidator 用于验证字段的值唯一 password = serializers.CharField( style={"input_type":"password"},help_text="密码",label="密码",write_only=True ) # style={"input_type":"password"} 用于说明该字段是 password 的类型(前端输入的时候会隐藏);write_only=True 不能返回给前端 # 重构 create() 方法,让密码以密文保存 def create(self, validated_data): user = super(UserRegSerializer, self).create(validated_data) user.set_password(validated_data["password"]) # 重置密码 user.save() return user class Meta: model = UserInfo fields = ("username","code","mobile","password") # code 和 password 这两个字段不会发送给前端(由于 write_only )