首页 > 其他分享 >drf

drf

时间:2023-09-14 16:33:49浏览次数:24  
标签:return name self request 序列化 data drf

目录

接口规范

Web API接口

  • 什么是Web API接口
# 为了在团队内部形成共识、防止个人习惯差异引起的混乱,我们需要找到一种大家都觉得很好的接口实现规范,而且这种规范能够让后端写的接口,用途一目了然,减少双方之间的合作成本。

# 通过网络,规定了前后端信息交互规则的url链接,也就是前后端信息交互的媒介,它称之为Web API接口
	-前端:向后端发送请求,获取数据   127.0.0.1:8080/index  --->返回数据
    -后端:请求某个地址,返回固定的数据

    
# Web API接口简单概括有下面四大特点
1.url:长得像返回数据的url链接
    	127.0.0.1:8080/index
2.有不同的请求方式:get、post、put、patch、delete
3.请求参数:json或xml格式的key-value类型数据
        127.0.0.1:8080/books?name=红楼梦   放在请求体中
4.响应结果: 一般是json格式,也可能是xml

  • 接口文档编写
接口文档资料参照:https://www.cnblogs.com/liuqingzheng/articles/17413678.html
# 作为后端,接口写好了
# 作为前端,需要使用我们写的接口(移动端,web,桌面端)

# 后端需要写接口文档
# 接口文档编写规范
-以用户注册接口为例:
  1 接口描述
  2 请求地址
  3 请求方式
  4 编码格式:json,urlencoded,form-data
  5 请求参数:
    -请求头
    -请求地址参数
    -请求体参数
  6 返回格式示例
    返回参数说明
  7 其他(可有可无)
    错误码

# 接口文档的展现形式:
1 word ,md ,写好传到公司的某个平台,与前端共享
2 自动生成接口文档---》后端通过配置--》把所写的接口都自动生成---》地址--》访问这个地址就能看到所有接口文档
    # 自动生成接口文档
	-coreapi,swagger:drf-yasg     
    # coreapi自动生成
    第一步:pip install coreapi
    第二步:设置接口文档访问路径
    from rest_framework.documentation import include_docs_urls
    urlpatterns = [
        path('docs/', include_docs_urls(title='站点页面标题'))
    ]

    第三步:在视图中,加注释

    第四步:配置文件配置
    REST_FRAMEWORK = {'DEFAULT_SCHEMA_CLASS':  
                      'rest_framework.schemas.coreapi.AutoSchema'},
    
3 公司内部搭建接口文档平台
    	- 开源:Yapi,YApi是去哪网大前端技术中心的一个开源可视化接口管理平台,YApi项目可以搭建在任何本地或云服务器上,完成后台项目开发时的接口编写。为开发、测试等人员提供可视化的接口预览
            -https://zhuanlan.zhihu.com/p/366025001
        - 自己开发(自研)
        
4 使用第三方平台(花钱)-->showdoc ....

接口测试工具

# 写好的接口,要测试,可以使用浏览器来做,但是浏览器只能发送get请求,接口有其他请求方式

# 专门的接口测试工具(使用习惯,大致一样)
    -postman,大部分公司用的,原来免费, 后来收费了,但是咱们只用免费部分就够了
    -postwomen
    -apifox
    
    
# 安装:下载安装包,双击运行,就安装完成

# 可以注册,登录后使用
#也可以跳过等, 直接使用
#不自动匹配/


# post请求,有多种编码格式
    -urlencoded 格式    ---》默认格式  b'xx=xx&yy=yy'
    -form-data 格式     ---》传文件格式,也可以带数据
    	----------------------------251971281264627087498320--  带数据,带文件
    -json格式(用的最多)-->只能传数据   b'{"name":"lqz","age":18}'
    
    
 # 如果是json形式提交数据----》django中想取,从request.POST取出到的




  • 测试postman
# 1 用django写个demo---》postman测试
	-1 传用户名密码到后端,查询数据库,都对了---》返回json格式{code:100,msg:登录成功}---》urlencoded
    -2 打印 request.POST
	-3 打印 request.body
    
   
    def index(request):
    back_dic={'code':200,'msg':'登录成功'}
    name=request.POST.get('name') #jack
    password=request.POST.get('password') #123

    #查询数据库用户信息与之比较
    res=models.User.objects.filter(name=name,password=password)
    if res:
        print(name)
        print(password)
        return JsonResponse(back_dic)
    else:
        back_dic={'code':201,'msg':'用户名或密码错误'}
        return JsonResponse(back_dic)
    
# 2 写一个接口,可以上传文件{code:100,msg:上传成功}
	-request.POST---->有没有取决于前端传没传数据
    -request.body--->小文件没事,大文件报错
  

@csrf_exempt
def files(request):
    print('body>', request.body)
    print('POST>', request.POST)  # 针对文件数据不在封装到qs中
    file_obj = request.FILES.get('myfile')
    # print(file_obj.name) #课堂随记.txt
    # print(file_obj,type(file_obj))#<class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
    base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    dir_path = os.path.join(base_dir, 'file_dir')
    if not os.path.exists(dir_path):
        os.mkdir(dir_path)
    files_dir=os.path.join(dir_path,file_obj.name)

    with open(files_dir, 'wb') as f:
    # with open(f'{dir_path}/{file_obj.name}', 'wb') as f:
        for line in file_obj:
            f.write(line)
    return JsonResponse({'code': 200, 'msg': '上传成功'})


    
# 3 -测:json格式---》request.POST 取不到,-request.data  默认没有---》(装饰器)--->无论前端什么格式:josn,urlencoded---》都取出来,是字典
    
#方式一
def authenticated(func):
    def inner(request, *args, **kwargs):
        # if request.method == 'POST':
        if request.POST: #其他格式form-data
            request.data=request.POST
            print(request.POST) #<QueryDict: {'name': ['jack']}>
        else: #json格式
            js_str = request.body.decode('utf8')
            request.data = json.loads(js_str)
            print(request.data) #{'name': 'jack'}
        return func(request, *args, **kwargs)
    return inner
    
from django.views.decorators.csrf import csrf_exempt #取消验证csrf中间件
@csrf_exempt
@authenticated
def js(request):
    return JsonResponse(request.data)




#方式二
def wraper(func):
    def inner(request,*args,**kwargs):
        try:
            request.data=json.loads(request.body)
        except:
            request.data=request.POST
        return func(request, *args, **kwargs)
    return inner


@csrf_exempt
@wraper
def test(request):
    name=request.data.get('name')
    password=request.data.get('password')
    user=models.User.objects.filter(name=name,password=password).first()
    if user:
        return JsonResponse({'code':'200','msg':'登录成功'})

    else:
        return JsonResponse({'code':'201','msg':'用户名或密码错误'})

Restful 接口规范

  • 简介
RESTful是一种定义API接口的设计风格,API接口的编写规范,尤其适用于前后端分离的应用模式中

这种风格的理念认为后端开发任务就是提供数据的,对外提供的是数据资源的访问接口,所以在定义接口时,客户端访问的URL路径就表示这种要操作的数据资源

我们可以使用任何一个框架都可以实现符合restful规范的API接口
  • 10条规范
1 数据的安全保障,通常使用https协议进行传输,它比http更安全
2 url地址中带接口标识:一般这样
  -https://api.baidu.com
  -https://www.baidu.com/api
        
3 多版本共存,url地址中带版本信息
  https://api.baidu.com/v1/login/
  https://api.baidu.com/v2/login/
        
4 数据即是资源,均使用名词:
  url地址尽量使用名词,不建议使用动词
  # 接口一般都是完成前后台数据的交互,交互的数据我们称之为资源
  https://api.baidu.com/users
  https://api.baidu.com/books
  https://api.baidu.com/book
  注:一般提倡用资源的复数形式,在url链接中不要出现操作资源的动词,错误示范:https://api.baidu.com/delete-user
  # 特殊的接口可以出现动词,因为这些接口一般没有一个明确的资源,或是动词就是接口的核心含义
  https://api.baidu.com/place/search
  https://api.baidu.com/login
        
5  资源操作由请求方式决定(method)
  #操作资源一般都会涉及到增删改查,我们提供请求方式来标识增删改查动作(get请求数据 post新增数据 put修改数据 delete删除数据)
  https://api.baidu.com/books   - get请求:获取所有书
  https://api.baidu.com/books/1 - get请求:获取主键为1的书
  https://api.baidu.com/books   - post请求:新增一本书书
  https://api.baidu.com/books/1 - put请求:整体修改主键为1的书
  https://api.baidu.com/books/1 - delete请求:删除主键为1的书
        
6  url地址中带过滤条件 
  # 只针对于搜索所有接口?后带过滤条件
  https://api.baidu.com/books -get请求表示查询所有图书,要查名字中有红的图书
  https://api.baidu.com/books?name_contains=红
  https://api.example.com/v1/zoos?limit=10:指定返回记录的数量
  https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置
  https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数
  https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
  https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件
        
        
7 响应状态码(http响应中带状态码)
  -http的响应状态码:https://blog.csdn.net/meng2lin/article/details/128955775
  -1xx:请求正在处理
  -2xx:请求成功 200  201
  -3xx:重定向
  -4xx:客户端错误
  -5xx:服务端出错
  -http的响应的数据中带状态码(公司自己规定的)
        	{
            "code": 101,
            "msg": "用户名或密码错误"
        }
        
8 响应的数据中带错误信息
	{
            "code": 101,
            "msg": "用户名或密码错误"
        }
        
9 返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范
  GET  /books:返回资源对象的列表(数组)
    		-[{name:金金金,price:88},{name:西游记,price:88}]
        	-{code:100,msg:成功,data:[{name:金金金,price:88},{name:西游记,price:88}]}
  GET /books/1:返回单个资源对象
        	-{name:金金金,price:88}    ---{code:100,msg:成功,data:{name:金金金,price:88}}
  POST /books:返回新生成的资源对象
        	-{id:4,name:金金金,price:88}  ---{code:100,msg:成功}
  PUT /books/4:返回完整的资源对象
        	-{id:4,name:金金金,price:188}  ---{code:100,msg:修改成功}
  DELETE /books/4: 返回一个空文档      ---{code:100,msg:删除成功}
           
10 响应的结果中带url链接

Django Rest_Framework

drf(djangorestframework:drf)为django中的第三方模块,只能用在django中帮助我们,快速实现符合resful规范的接口
# book表为例,写5个接口(后面写的所有接口,都是这5个,及其变形)
	-查询所有图书
    -新增一本图书
    -修改一本图书
    -查询一本图书
    -删除一本图书
  • 下载安装
1.安装模块
pip3.8 install djangorestframework==稍微降版本
# 如果是django2,直接这样装,装最新drf,他们不匹配---》pip会自动把旧django卸载,安装最新版本的django,安装最新drf

# django3 ,这样没有任何问题
pip3.8 install djangorestframework
  • 快速使用
# 注册
在app中注册
    INSTALLED_APPS = [
        'rest_framework',  # 一定不要忘了加  ,
    ]

# 路由
from app01.views import BookView
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
router.register('books', BookView, 'books')
urlpatterns = [
]
urlpatterns += router.urls


# 视图
from .serializer import BookSerializer
from rest_framework.viewsets import ModelViewSet
class BookView(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer


# 序列化类
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'
# 1  book的剩余3个接口
views.py
import json
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
from django.views import View
from app01 import models
from django.http import JsonResponse


class Bookview(View):
    print('123')

    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)

    def get(self, request):  # 查询所有数据
        print('get')
        books = models.Books.objects.all()
        # print(books)
        lis = []
        for items in books:
            lis.append({'title': items.title, 'price': items.price})
        res = {'code': 200, 'msg': '查询成功', 'data': lis}
        return JsonResponse(res)

    def post(self, request):  # 新增数据
        print('post')
        title = request.POST.get('title')
        price = request.POST.get('price')
        models.Books.objects.create(title=title, price=price)
        books_object = models.Books.objects.all()

        lis = []
        for items in books_object:
            lis.append({'title': items.title, 'price': items.price})
        res = {'code': 200, 'msg': '查询成功', 'data': lis}
        return JsonResponse(res)


class Bookdetailview(View):
    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)

    def get(self, request, pk):  # 获取相应主键的数据
        print('get')
        book = models.Books.objects.filter(pk=pk).first()
        # print(book)
        if book:
            return JsonResponse({'code': 200, 'msg': '查询成功', 'data': {book.title: book.price}})
        else:
            return JsonResponse({'code': 200, 'msg': '数据不存在'})

    def put(self, request, pk):  # 修改相应主键的数据
        print('put')
        try:
            # b'title=ooo&price=88'
            b = request.body.decode('utf8').split('&')  # ['title=ooo', 'price=88']
            res = {i.split("=")[0]: i.split("=")[1] for i in b}  # {'title': 'ooo', 'price': '88'}
            title = res.get('title')
            price = res.get('price')
            models.Books.objects.filter(pk=pk).update(title=title, price=price)
            return JsonResponse({'code': 200, 'msg': '修改成功', 'data': res})


        except:
            print(request.body) #b'{"title":"hhaha","price":"99"}'
            res = request.body.decode('utf8') #{"title":"hhaha","price":"99"}
            title = json.loads(res).get('title')
            price = json.loads(res).get('price')
            models.Books.objects.filter(pk=pk).update(title=title, price=price)
            return JsonResponse({'code': 200, 'msg': '修改成功', 'data': {'title': title, 'price': price}})


    def delete(self, request, pk):  # 删除相应主键数据
        print('delete')
        res = models.Books.objects.filter(pk=pk).delete()  # res结果为影响的行数
        if res[0] == 1:  # res结果为1条则有数据
            return JsonResponse({'code': 200, 'msg': '删除成功'})
        else:  # res结果为0则没有数据
            return JsonResponse({'code': 200, 'msg': '数据不存在'})

        
        
        
urls.py
from app01 import views
urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/<int:pk>/', views.Bookdetailview.as_view()),
    #转换器:str,int ,path    http://127.0.0.1/books/<转化名字:变量>
    path('books/', views.Bookview.as_view()),
]

序列化组件

序列化和反序列化

# api接口开发,最核心最常见的一个过程就是序列化
	# 序列化: 把我们识别的数据转换成指定的格式提供给别人。
	例如:我们在django中获取到的数据默认是模型对象(queryset),但是模型对象数据无法直接提供给前端或别的平台使用,所以我们需要把数据进行序列化,变成字符串或者json数据,提供给别人。

	# 反序列化:把别人提供的数据转换/还原成我们需要的格式。
	例如:前端js提供过来的json数据,对于python而言就是字符串,我们需要进行反序列化换成模型类对象,这样我们才能把数据保存到数据库中
    
    
# 序列化:drf称为 read      序列化
# 反序列化:drf称为 write   反序列化

序列化组件介绍

基于原生djagno写接口序列化,自己用for循环做比较麻烦,借助于drf提供的序列化组件来完成快速序列化

## 使用步骤
	1 先在配置文件中注册 :
        INSTALLED_APPS = [
        'rest_framework',
        ]
        
    2 写一个序列化类--》新建一个py文件---》serializer.py
    	-继承drf提供的serializers.Serializer        
        -在类中写要序列化的字段:字段类---》跟之前学过的models.py中的字段类完全对应,但是比models多
        from rest_framework import serializers
		class UserSerializer(serializers.Serializer):
    	name = serializers.CharField()
    	hobby = serializers.CharField()
    	password=serializers.CharField()
    	age=serializers.IntegerField()

        
    3 在视图类中,使用序列化类
    	多条:serializer=UserSerializer(instance=users,many=True)
        单条:serializer=UserSerializer(instance=user)
        
    4 拿到序列化后的数据
    	serializer.data  可能是列表,可能是字典
    
    5 使用drf提供的Resposne 返回
    	from rest_framework.response import Response
        ...
        return Response(...)

快速使用

  • 路由
urlpatterns = [
    path('users/', UserView.as_view()),
    path('users/<int:pk>', UserDetailView.as_view()),
]
  • 视图类
from .models import User
from .serializer import UserSerializer
from rest_framework.response import Response
class UserView(APIView):
    def get(self,request):
        users=User.objects.all()
        # 之前用for循环,现在用序列化类
        # 传了两个参数:instance 要序列化的对象(qs,单个对象)   many=True表示序列化多条,如果不写就是序列化一条
        ser=UserSerializer(instance=users,many=True)
        # 拿到序列化后的数据  ser.data--->多条就是列表  单条字典
        """
        return JsonResponse(ser.data,safe=False)
        之前我们通过使用return JsonResponse,ser.data返回的数据可能是字典、列表,而列表形式我们又需要添加safe=False较为麻烦,在drf中提供响应对象Response,需要导入from rest_framework.response import Response,使用Response后ser.data返回的任何数据则都不用再添加safe=False
        """        
        return Response(ser.data)

class UserDetailView(APIView):
    def get(self,request,pk):
        user=User.objects.all().filter(pk=pk).first()
        # 传了两个参数:instance 要序列化的对象   many=True表示序列化多条
        ser=UserSerializer(instance=user)
        return Response(ser.data)
  • 序列化类
from rest_framework import serializers


class UserSerializer(serializers.Serializer):
    # 写要序列化的字段
    name = serializers.CharField()
    # hobby = serializers.CharField()
    # password=serializers.CharField()
    age=serializers.IntegerField()

常用字段类和参数

  • 常用字段类
# 写序列化类的时候,写了CharField,IntegerField  跟django中models中的类似

# 序列化类中的和models中的一一对应,但是序列化类中多一些

# 多的--->暂时有个印象,后面会详细讲
ListField
DictField

# ListField\DictField使用场景
	{name:金鹏没,price:99,publish:{name:xx出版社,addr:南京},authors:[{},{}]}
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)
BooleanField BooleanField()
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=)
  • 常用字段参数
# 字段类上,可以传参数,是做反序列化校验用的
	CharField:max_length,min_lenght,allow_blank: 可以不传
    IntegerField:max_value,min_value
    
 # 所有字段都可以用通用的
	-非常重要:read_only,write_only
    -default,required,allow_null
max_length 最大长度
min_lenght 最小长度
allow_blank 是否允许为空
trim_whitespace 是否截断空白字符
max_value 最小值
min_value 最大值
参数名称 说明
read_only 表明该字段仅用于序列化输出,默认False
write_only 表明该字段仅用于反序列化输入,默认False
required 表明该字段在反序列化时必须输入,默认True
default 反序列化时使用的默认值
allow_null 表明该字段是否允许传入None,默认False
validators 该字段使用的验证器
error_messages 包含错误编号与错误信息的字典
label 用于HTML展示API页面时,显示的字段名称
help_text 用于HTML展示API页面时,显示的字段帮助提示信息

序列化组件之校验

# 序列化组件
	- 序列化
    - 反序列化
    - 反序列化校验  
    
    
    
# 反序列化之校验:    ser.is_valid()
	1 字段自己的校验规则(字段类的属性上) 
    2 局部钩子(给某个字段加校验规则)  
    3 全局钩子
    
    
# 反序列化保存
	ser.save()---》必须序列化类中重写 create--》自己定保存到哪个表
    def create(self, validated_data):  # validated_data:前端传入,校验过后的数据
        user = User.objects.create(**validated_data)
        return user

视图类

from .models import User
from .serializer import UserSerializer
from rest_framework.response import Response


class UserView(APIView):
    def get(self, request):
        users = User.objects.all()
        # 之前用for循环,现在用序列化类
        # 传了两个参数:instance 要序列化的对象(qs,单个对象)   many=True表示序列化多条,如果不写就是序列化一条
        ser = UserSerializer(instance=users, many=True)
        # 拿到序列化后的数据  ser.data--->多条就是列表  单条字典
        return Response(ser.data)

    def post(self, request):
        # 前端提交过来的数据 request.data
        ser = UserSerializer(data=request.data)
        # 校验数据--》3层: 1 字段自己的校验规则(字段类的属性上)  2 局部钩子(给某个字段加校验规则)  3 全局钩子
        if ser.is_valid():
            # 保存
            ser.save()  # 会报错,序列化类中重写create方法

            return Response({'code': 100, 'msg': '保存成功'})
        else:  # ser.errors 校验失败错误的数据
            return Response({'code': 101, 'msg': ser.errors})


class UserDetailView(APIView):
    def get(self, request, pk):
        user = User.objects.all().filter(pk=pk).first()
        # 传了两个参数:instance 要序列化的对象   many=True表示序列化多条
        ser = UserSerializer(instance=user)
        return Response(ser.data)

序列化类

# 写序列化类
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from .models import User


class UserSerializer(serializers.Serializer):
    # 写要序列化的字段
    # 字段自己
    name = serializers.CharField(max_length=8, min_length=3, required=True)
    hobby = serializers.CharField()
    password = serializers.CharField()
    age = serializers.IntegerField()

    # 局部钩子写一个方法  def validate_字段名,传入要校验的数据--》前端传入的
    """针对局部钩子一个字段只能写一个方法"""
    def validate_name(self, value):
        if value.startswith('sb'):
            raise ValidationError('不能以sb开头')  # 如果校验失败,抛ValidationError
        return value  # 如果校验通过,返回 value,后续继续用

    # 全局钩子 写一个方法名为 def validate,用于多个字段的同时校验  
    """全局钩子不能写多个,当写多个def validate方法执行始终以最后的全局钩子方法为准,要想校验多种必须写在一个def validate方法中"""
    def validate(self, attrs):  # 前端传入的所有数据,校验过后attrs 字典
        name = attrs.get('name')
        hobby = attrs.get('hobby')
        if name == hobby:
            raise ValidationError('名字和爱好不能一样') # 如果校验失败,抛ValidationError
        else:
            return attrs # 如果校验通过,返回 attrs

    # 重写create方法
    def create(self, validated_data):  # validated_data:前端传入,校验过后的数据
        user = User.objects.create(**validated_data)
        return user

路由

urlpatterns = [
    path('users/', UserView.as_view()),
    path('users/<int:pk>', UserDetailView.as_view()),
]

基于APIVIew+Response+序列化类的5个接口

  • views.py
from django.shortcuts import render

# Create your views here.

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Book
from .serializer import BookSerializer


class BookView(APIView):
    # 查询所有接口
    def get(self, request, *args, **kwargs):
        book_list = Book.objects.all()
        ser = BookSerializer(book_list, many=True)
        return Response({'code': 100, 'msg': '查询成功', 'result': ser.data})

        # 新增一条

    def post(self, request, *args, **kwargs):
        ser = BookSerializer(data=request.data)
        if ser.is_valid():
            ser.save() # 调用了序列化类的save:内部会触发序列化类中 create方法的执行     不是book.save()
            return Response({'code': 100, 'msg': '新增成功'})
        else:
            return Response({'code': 100, 'msg': ser.errors})


class BookDetailView(APIView):
    # 查询单条
    def get(self, request, pk, *args, **kwargs):
        book = Book.objects.all().filter(pk=pk).first()
        ser = BookSerializer(book,many=False)
        return Response({'code': 100, 'msg': '查询单条成功', 'result': ser.data})

        # 修改一条

    def put(self, request, pk, *args, **kwargs):
        # 要用查出来的对象,使用传入的数据,做修改
        book=Book.objects.filter(pk=pk).first()
        ser = BookSerializer(instance=book,data=request.data) # 使用前端传入的数据,修改book
        if ser.is_valid():
            ser.save()  # 调用了序列化类的save:内部会触发序列化类中 update方法的执行     不是book.save()
            return Response({'code': 100, 'msg': '修改成功'})
        else:
            return Response({'code': 100, 'msg': ser.errors})

    def delete(self, request, pk, *args, **kwargs):
        Book.objects.filter(pk=pk).delete()
        return Response({'code': 100, 'msg': '删除成功'})

  • serializer.py
class BookSerializer(serializers.Serializer):
    # 这两个即做序列化又做反序列化
    name = serializers.CharField()
    price = serializers.IntegerField()

    # 这俩只做序列化
    publish_dict = serializers.DictField(read_only=True)  # 只做序列化
    author_list = serializers.ListField(read_only=True)  # 只做序列化

    # 这俩只做反序列化
    publish = serializers.IntegerField(write_only=True)  # 反序列化
    authors = serializers.ListField(write_only=True)  # 反序列化

    def create(self, validated_data):  # {"name":"111金金金111","price":999,"publish":1,"authors":[1,2]}
        book = Book.objects.create(name=validated_data.get('name'), price=validated_data.get('price'),
                                   publish_id=validated_data.get('publish_id'))
        # 增加中间表的记录:图书和作者的关系
        book.authors.add(*validated_data.get('authors'))  # 向中间表中存入:这个图书关联的做作者
        return book


    def update(self, instance, validated_data):
        # Book.objects.filter(pk=instance).update(**validated_data)
        # instance 待修改的对象   咱们在 view中的那个book
        # validated_data 校验过后的数据   本质还是  request.data 经过了数据校验
        # 方式一:
        # instance.name=validated_data.get('name')
        # instance.price=validated_data.get('price')
        # instance.publish=validated_data.get('publish')
        # instance.save()

        # 方式二: 反射是通过字符串动态的获取或设置属性或方法
        # get=getattr(self,'get')
        # get()

        # setattr(instance,'name','西游记') ---》 instance.name='西游记'
        for k in validated_data:  # {"name":"西游记","price":99,"publish":南京出版社}
            setattr(instance, k, validated_data.get(k))
            # instance.publish=validated_data.get('publish')
        instance.save()
        return instance    
  • models.py
from django.db import models

class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.IntegerField()
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)

    # 多对多关系---》
    # authors 写了这个字段,在数据的book表中,不会有这个字段,而它的形式是 一个中间表
    authors = models.ManyToManyField(to='Author')

    @property
    def publish_dict(self):
        return {'name': self.publish.name}

    @property
    def get_name(self):
        return self.name + '-NB'

    def author_list(self):
        l = []
        for author in self.authors.all():
            l.append({'name': author.name, 'sex': author.sex, 'age': author.age})
        return l


class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    sex = models.CharField(max_length=32)

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

  • 总结
#1.序列化类
  多条一定要写many=True
#2.序列化校验
  ser.is_valid()走校验
#3.反序列化保存
-新增:
  ser = BookSerializer(data=request.data)
  ser.save()会触发序列化类中create方法,在源码内部根据是否有instance来判断是执行create还是update方法,没有执行create方法,有则执行update方法
  create中,自己写保存到哪个表中:有数据--》保存到某个表中
-修改:
  ser = BookSerializer(instance=待修改对象,data=request.data)
  ser.save()--->触发 序列化类中的 update---》为什么?内部做了判断:根据是否有instance
  update中,有待修改对象,有数据---》修改完保存即可--》两种方式

反序列化之更新

  • view.py
class BookDetailView(APIView):
    # 查询单条
    def get(self, request, pk, *args, **kwargs):
        book = Book.objects.all().filter(pk=pk).first()
        ser = BookSerializer(book,many=False)
        return Response({'code': 100, 'msg': '查询单条成功', 'result': ser.data})


    def put(self, request, pk, *args, **kwargs):
        # 要用查出来的对象,使用传入的数据,做修改
        book=Book.objects.filter(pk=pk).first()
        ser = BookSerializer(instance=book,data=request.data) # 此段代码为使用前端传入的数据(data=request.data)来修改book(instance=book)
        """触发序列化类create和update的区别在于源码中判断是否有instance,有instance触发update反之触发create"""
        if ser.is_valid():
            ser.save()  # 调用了序列化类的save:内部会触发序列化类中 update方法的执行     不是book.save()
            return Response({'code': 100, 'msg': '修改成功'})
        else:
            return Response({'code': 100, 'msg': ser.errors})

    def delete(self, request, pk, *args, **kwargs):
        Book.objects.filter(pk=pk).delete()
        return Response({'code': 100, 'msg': '删除成功'})
  • serializer.py
class BookSerializer(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.IntegerField()
    publish = serializers.CharField()

    # 重写create  前端传入,校验过后的数据validated_data
    def create(self, validated_data):
        book = Book.objects.create(**validated_data)  # 前端传入的key键必须要与数据库中的字段名一致,否则字段打散后key键对应不上表字段名
        return book

    # 如果是修改,需要重写update方法
    def update(self, instance, validated_data):
        # instance 为待修改的对象就是在视图类中的那个book(instance=book); validated_data为校验过后的数据,本质还是request.data 经过了数据校验
        # instance 就是pk  2
        # Book.objects.filter(pk=instance).update(**validated_data)

        # 方式一:
        # instance.name=validated_data.get('name')
        # instance.price=validated_data.get('price')
        # instance.publish=validated_data.get('publish')
        # instance.save()

        # 方式二: 反射是通过字符串动态的获取或设置属性或方法
        for k in validated_data:  # {"name":"西游记","price":99,"publish":南京出版社}
            setattr(instance, k, validated_data.get(k))
            # setattr等同于:      
            # instance.name=validated_data.get('name')
        	# instance.price=validated_data.get('price')
        	# instance.publish=validated_data.get('publish')
        instance.save()
        return instance

source用法

serializers.py
class BookSerializer(serializers.Serializer):
    # 用法一:最简单,拿表中的字段
    xxx = serializers.CharField(source='name')
    # 用法二 :跨表查询
    publish = serializers.CharField(source='publish.name') # source='publish.name'自动对应成出版社的名字 可以通过 .  跨表查询
    #用法三:表模型中写方法,拿到方法的返回值
    yyy = serializers.CharField(source='get_name')
    ### models.py中
    class User(models.Model):
    	name=models.CharField(max_length=64)
    
        @property
        def get_name(self):
            return self.name+'sb'

    
    
# 没使用source参数前端看到:
    {
        "name": "西游记",
        "price": 199,
        "publish": "南京出版社"
    },    
    
# 使用用法一source参数后前端看到:
    {
        "xxx": "西游记",
        "price": 199,
        "publish": "南京出版社"
    },
    
# 使用用法三source参数后前端看到:
    {
        "name": "西游记",
        "price": 199,
        "publish": "南京出版社"
        "yyy":"西游记NB"
    },    

SerializerMethodField定制字段

# 定制返回的字段格式
  		{
            "name": "信息",
            "price": 12,
            "publish": {name:xx,addr:xx}
        }
    
# 方案一:使用SerializerMethodField 定制
-步骤1在序列化类中使用SerializerMethodField
publish_detail = serializers.SerializerMethodField()
-步骤2 配合步骤1在序列化类中写个方法 def get_步骤1的字段名
def get_publish_detail(self, obj): # 返回什么,序列化后publish就是什么,obj 就是序列化到的book对象
  return {'name':obj.publish.name,'addr':obj.publish.addr}
    
 # 方案二: 在表模型中定制
-步骤1 表模型中写方法,方法名与序列化类同名,包装成数据属性
@property
def publish_dict(self):#self为视图类中instance的对象
  return {'name': self.publish.name}
-步骤2 序列化类中
publish_dict=serializers.DictField()
  • serializers.py
class BookSerializer(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.IntegerField()
    # publish = serializers.CharField(source='publish.name')
    # author = serializers.CharField(source='author.name')

    # 用法一
    publish_detail=serializers.SerializerMethodField()
    def get_publish_detail(self,obj):
        return {'name':obj.publish.name,'addr':obj.publish.addr}

    # 用法二
    publish_dict=serializers.DictField() 
  • models.py
class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.IntegerField()
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    author=models.ManyToManyField(to='Author')

    def publish_dict(self): #配合用法二使用
        return {'name':self.publish.name,'addr':self.publish.addr}
  • 前端看到的定制的格式
"data": [
        {
            "name": "王国",
            "price": 28,
            "publish_dict": {
                "name": "南京出版社",
                "addr": "南京"
            }
        }, 

多表关联序列化和反序列化

# 一个序列化类,想即做序列化,又做反序列化,会出现问题:字段不匹配,尤其是多表关联的字段

# 字段 即做序列化,又做反序列化
	name = serializers.CharField()
    price = serializers.IntegerField()
    
# 字段(定制字段):只做序列化(read_only=True表示 只做序列化)
	publish_dict = serializers.DictField(read_only=True)  # 只做序列化
    author_list = serializers.ListField(read_only=True)  # 只做序列化
    
# 字段只做反序列化(write_only=True);字段类型取决于前端传入的格式
	publish_id = serializers.IntegerField(write_only=True)  # 反序列化
    authors = serializers.ListField(write_only=True)  # 反序列化
    """反序列化字段也可以做局部钩子
    def validate_publish_id(self,value):
    	pass
    return value     
    """
    
    
# 保存方法需要自己重写
	def create(self, validated_data):  # {"name":"111金金金111","price":999,"publish":1,"authors":[1,2]}
        authors=validated_data.pop('authors')
        book = Book.objects.create(**validated_data)
        # 增加中间表的记录:图书和作者的关系
        book.authors.add(*authors)  # 向中间表中存入:这个图书关联的做作者
        """多对多外键增删改查 增add 删clear\remove 改set必须放容器类型列表、元组"""

        return book
    
    
# 修改方法    
    def update(self, instance, validated_data):
        author = validated_data.pop('author')
        for k in validated_data:
            setattr(instance, k, validated_data[k])
            instance.publish_id = validated_data.get('publish_id')
        instance.save()
        instance.author.set(author)

        return instance   

反序列化校验

# 反序列化,有三层校验
    -1 字段自己的(写的字段参数:required   max_length 。。。)
    -2 局部钩子:写在序列化类中的方法,方法名必须是 validate_字段名
    	def validate_name(self, name):
            if 'sb' in name:
                # 不合法,抛异常
                raise ValidationError('书名中不能包含sb')
            else:
                return name
    -3 全局钩子:写在序列化类中的方法 方法名必须是 validate
       def validate(self, attrs):
            price = attrs.get('price')
            name = attrs.get('name')
            if name == price:
                raise ValidationError('价格不能等于书名')
            else:
                return attrs
            
  # 只有三层都通过,在视图类中:
	ser.is_valid():  才是True,才能保存

视图组件

APIView

#1  使用drf,以后都写cbv---》继承一个视图类---》以后都继承drf提供 的APIView

#2  APIView 继承了djagno的View

#3  补充:这三个都是一个
from django.views import View
from django.views.generic import View
from django.views.generic.base import View



# 4 继承APIView写cbv---》执行起来跟之前效果一样---》内部发生了非常大的变化
	1 写视图类
    from rest_framework.views import APIView
    class PublishView(APIView):
        def get(self, request):
            return JsonResponse({'code': 999})
    2 配置路由
    path('publish/', PublishView.as_view()),
    
    3.配置文件注册
    INSTALLED_APPS = [
            'rest_framework',]
    

基于APIView的5个接口

  • 视图类
from rest_framework.views import APIView  # APIView继承了djagno原来的View
from .serializer import BookSerializer
from rest_framework.response import Response


class BookView(APIView):
    # 查询所有
    def get(self, request):
        book_list = Book.objects.all()
        # drf提供了序列化类(先别关注)
        ser = BookSerializer(instance=book_list, many=True)  # 序列化
        return Response({'code': 100, 'msg': '成功', 'result': ser.data})

    def post(self, request):
        ser = BookSerializer(data=request.data)  # 反序列化
        if ser.is_valid():  # 数据校验---》有些不合法的禁止
            ser.save()  # 保存到数据库中
        return Response({'code': 100, 'msg': '成功'})


class BookDetailView(APIView):
    # 查询单条
    def get(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(instance=book, many=False)  # 序列化
        return Response({'code': 100, 'msg': '成功', 'result': ser.data})

    # 修改一条
    def put(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(instance=book, data=request.data)  # 反序列化
        if ser.is_valid():  # 数据校验---》有些不合法的禁止
            ser.save()  # 保存到数据库中
        return Response({'code': 100, 'msg': '成功'})

    def delete(self, request, pk):
        Book.objects.filter(pk=pk).delete()
        return Response({'code': 100, 'msg': '删除成功'})
  • 序列化类
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'
  • 路由
urlpatterns = [
    path('books/', BookView.as_view()),
    path('books/<int:pk>/', BookDetailView.as_view()),
]

CBV源码分析

# cbv写法:
	1 视图中写视图类,继承View,写跟请求方式同名的方法
    	class BookView(View):
            def get(self,request):
                return 四件套
     2 在路径用写
    	path('books/', BookView.as_view())
        
        
        
# 如上写法,为什么能够执行

# 前置条件:前端请求,一旦路径匹配成功,就会执行  BookView.as_view()(request传入,)
# 入口在  BookView.as_view()--->执行结果---》View中有个as_view类的绑定方法
    @classmethod
    def as_view(cls, **initkwargs):
        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            res=self.dispatch(request, *args, **kwargs)
            return res
        return view
    
# 执行结果是view 的内存地址: 请求来了,执行view(request)
	path('books/', view)
    
# 执行 View类中的as_view方法中的内层的view函数,路由匹配成功,本质是在执行
	self.dispatch(request, *args, **kwargs)
    # self是谁的对象?BookView的对象
    # 去BookView中dispatch,找不到,去父类,View中找到了
    
    
# View这个类的dispatch
    def dispatch(self, request, *args, **kwargs):
        # request.method.lower() 如果是get请求,  ‘get’ 在这个列表里面
        if request.method.lower() in self.http_method_names:
            # handler=getattr(BookView的对象,'get')   
            # handler就是BookView类中的get方法
            handler = getattr(self, request.method.lower())
        else:
            handler = self.http_method_not_allowed
        # 执行 BookView类中的get方法 (request)
        return handler(request, *args, **kwargs)
    
# 最终本质跟写fbv的执行流程一样
# 最终结论:什么请求方式,就会执行视图类中的什么方法

APIView源码分析

# 有了drf,后期都写CBV,都是继承APIView及其子类
# 执行流程:
	-入口:path('books/', BookView.as_view())---》请求来了,执行BookView.as_view()(request)
    -as_view 是谁的? APIView的as_view
        @classmethod
        def as_view(cls, **initkwargs):
            # super()代指的是:父类对象  View类的对象
            # View的as_view(**initkwargs)----》执行结果是view,是View类的as_view方法中的view
            view = super().as_view(**initkwargs)
            view=csrf_exempt(view)  # 局部禁用csrf,
            return view
        
  	-path('books/', View类的as_view中的view,只是去掉了csrf的认证)
	-请求来了,执行 【View类的as_view中的view,只是去掉了csrf的认证(request)】
    -执行:self.dispatch(request, *args, **kwargs),  self要从根上找
	-self.dispatch 是APIView的dispatch,源码如下
        def dispatch(self, request, *args, **kwargs):
            # request 是新的request,      request是老的request
            request = self.initialize_request(request, *args, **kwargs)
            self.request = request
            try:
                # 执行了认证,权限和频率
                self.initial(request, *args, **kwargs)
                # 在执行视图类方法之前,去掉了csrf认证,包装了新的request,执行了认证频率和权限
                #### 执行请求方式字符串对应的方法
                if request.method.lower() in self.http_method_names:
                    handler = getattr(self, request.method.lower(),
                                      self.http_method_not_allowed)
                else:
                    handler = self.http_method_not_allowed
                response = handler(request, *args, **kwargs)
            except Exception as exc:
                response = self.handle_exception(exc)
                
            # 无论是在三大认证,还是视图类的方法中,出现错误,都会被异常捕获,统一处理
            self.response = self.finalize_response(request, response, *args, **kwargs)
            return self.response
    

    
# 总结:
	1  以后只要继承APIView的所有视图类的方法,都没有csrf的校验了
    2  以后只要继承APIView的所有视图类的方法 中的request是新的request了
    3  在执行视图类的方法之前,执行了三大认证(认证,权限,频率)
    4  期间除了各种错误,都会被异常捕获,统一处理

请求与响应

路由组件

认证权限频率

过滤排序分页

JWT认证

标签:return,name,self,request,序列化,data,drf
From: https://www.cnblogs.com/Super-niu/p/17694724.html

相关文章

  • drf整合
    drf内容简介1.drf入门规范2.序列化组件----(最重要)3.请求与响应4.视图组件----(重要)5.路由组件6.认证、权限、频率----(重要)7.过滤、排序、分页、全局异常处理8.接口文档9.jwt认证10.权限:ACL、RBAC drf入门规范前后端开发模式前后端混合开发模式: ......
  • drf-day12
    昨日回顾基于自定义用户表签发token1、前端(postman、web、appp、小程序)发送http请求,携带用户名和密码,通过中间件到达后端2、后端request.data取出用户名和密码3、拿着用户名和密码去数据库中查询,有没有4、如果有就说明登陆成功5、签发token:通过当前用户得到payload(自动生......
  • drf————源码分析
    drf————源码分析>认证源码分析权限源码分析频率类源码分析三大认证的源码分析之前读取的APIView的源码的执行流程中包装了新的request,执行了三大认证,执行视图类的方法,处理了全局异常查看源码的入口APIView的dispatch进入后在APIView的dispatch的496行上下self.......
  • drf-jwt自定义表签发、多方式登录
    一、jwt自定义表签发自定义表签发,用的是自己定义的表1.models.py:-注意点:因为视图中使用了drf-jwt的自动签发,所以用户名必须为usernamefromdjango.dbimportmodels#自定义签发用的是自定义的user表#注意点:使用drf-jwt的自动签发,用户名必须是usernameclassUser(......
  • drf-day11
    jwt自定义表签发models.pyfromdjango.dbimportmodelsfromdjango.contrib.auth.modelsimportAbstractUser#继承AbstractUser直接使用自动签发token#纯自己写的用户表,需要自己签发classUser(models.Model):username=models.CharField(max_length=32)......
  • drf - 过滤、排序、异常源码剖析、jwt
    过滤类的源码剖析1、为什么在视图类中配置了一个过滤类,就可以走? -filter_backends=[SearchFilter,MyFilter]2、前提条件是必须继承在视图类中继承GenericAPIView: 因为filter_backends是GenericAPIView的类属性。3、如果光继承了GenericAPIView还是不行,还需要再继承List......
  • drf- 过滤、排序、异常处理
    session的执行流程写一个登录接口----->保存用户的登录状态 -获取到用户名,密码-使用request.session["username"]=用户名、或者request.session["pk"]=pk值-签发阶段做了三件事: -1、生成一个随机的字符串-2、在django_session表中保存 -se......
  • drf- 三大认证、排序
    三大认证组件登录认证我们可以使用auth自带的User表,也可以选择自定义表.写登录接口,登录成功要有标志,生成一个随机的字符串,放到表中,以后只要携带这个字符串就可以登录成功。视图层方法一:classUserView(GenericViewSet):queryset=models.UserInfo.objects.all()......
  • 接口文档、jwt介绍和构成、jwt签发和认证、base64编码、drf-jwt编码、drf-jwt使用
    接口文档作为后端,接口写好了,需要编写接口文档作为前端,需要使用后端写的接口(移动端、web桌面端),可以照着接口文档写接口文档的展现形式:1.word、md,写好传到公司的某个平台===》前端可以下载使用2.自动生成接口文档===》后端通过配置===》把所写的接口都自动生成===》......
  • 接口文档,jwt介绍和构成,jwt签发与认证,base64编码,drf-jwt使用,django-rest-framewor
    1接口文档#作为后端,接口写好了#作为前端,需要使用我们写的接口(移动端,web,桌面端)#后端需要写接口文档#接口文档的展现形式: 1word,md,写好传到公司的某个平台---》前端可以下载2自动生成接口文档---》后端通过配置--》把所写的接口都自动生成---》地址--》访问......