1 djangorestframework:django的app,只能再django上使用
2 pip3 install djangorestframework
3 简单使用,看代码
django: 2.0.7 ,1版本也可以
djangorestframework:3.12.1(默认把Django更新至最新版本)
1 Representational State Transfer:表征性状态转移
2 Web API接口的设计风格,尤其适用于前后端分离的应用模式中
3 与语言,平台无关,任何框架都可以写出符合restful规范的api接口
4 规范:10条
-1 数据的安全保障:url链接一般都采用https协议进行传输
-2 接口特征表现:api关键字标识
-https://api.baidu.com/books/
-https://www.baidu.com/api/books/
-3 多版本共存:url链接中标识接口版本
-https://api.baidu.com/v1/books/
-https://api.baidu.com/v2/books/
-4 数据即是资源,均使用名词(可复数)***********
-接口一般都是完成前后台数据的交互,交互的数据我们称之为资源
-一般提倡用资源的复数形式,不要使用动词
-查询所有图书
-https://api.baidu.com/books/
-https://api.baidu.com/get_all_books/ # 错误示范
-https://api.baidu.com/delete-user # 错误的示范
-https://api.baidu.com/user # 删除用户的示例
-5 资源操作由请求方式决定:
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 - patch请求:局部修改主键为1的书
https://api.baidu.com/books/1 - delete请求:删除主键为1的书
-6 过滤,通过在url上传参的形式传递搜索条件
https://api.example.com/v1/zoos?limit=10 :指定返回记录的数量
https://api.example.com/v1/zoos?offset=10&limit=3:指定返回记录的开始位置
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 响应状态码
-返回数据中带状态码
-{
'code':100}
-8 返回结果中带错误信息
-{
'code':100,'msg':'因为xx原因失败'}
-9 返回结果,该符合以下规范
GET /collection:返回资源对象的列表(数组)
GET /collection/resource:返回单个资源对象(字典)
POST /collection:返回新生成的资源对象 (新增后的对象字典)
PUT /collection/resource:返回完整的资源对象 (修改后的对象字典)
PATCH /collection/resource:返回完整的资源对象 (修改后的对象字典)
DELETE /collection/resource:返回一个空文档 ()
-10 返回的数据中带链接地址
-查询id为1的图书接口,返回结果示例
{
'code':100,
'msg':'成功',
'result':
{
'title':'红楼梦',
'price':12.3,
'publish':'https://127.0.0.1/api/v1/publish/3'
}
}
1 APIview的as_view
-内部还是执行了View的闭包函数view
-禁用掉了csrf
-一切皆对象,函数也是对象 函数地址.name=wuxi
2 原生View类中过的as_view中的闭包函数view
-本质执行了self.dispatch(request, *args, **kwargs),执行的是APIView的dispatch
3 APIView的dispatch
def dispatch(self, request, *args, **kwargs):
# DRF的Request类的对象,内部有request._request,是原生request
request = self.initialize_request(request, *args, **kwargs)
self.request = request
try:
self.initial(request, *args, **kwargs)
'''
#认证,权限,频率
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(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)
# 把视图函数(类)返回的response,又包装了一下
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
1 Request类
-request._request:原生request
-request.data : post请求提交的数据(urlencoded,json,formdata)
-request.user :不是原生的user了
-request.query_params :原生的request.GET,为了遵循restful规范
-requset.FILES :新的
-重写了__getattr__,新的request.原来所有的属性和方法,都能直接拿到
def __getattr__(self, attr):
return getattr(self._request, attr)
#1 作用:
1. 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串
-Book--序列化器--->字典--同过drf:Response--》json格式字符串--->传给前端
2. 反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型
json格式数据---drf:Request-->字典---序列化器---》Book
3. 反序列化,完成数据校验功能
# 1 序列化的使用
-新建一个py文件,myserializer.py
-写一个序列化类继承serializers.Serializer
-在类中写要序列化的字段
-在视图类中,实例化得到一个序列化类的对象,把要序列化的数据传入
ser = models.Book.objects.all()
如果是queryset对象many=True
ser=BookSerializer(instance=res,many=True)
如果不是
ser=BookSerializer(instance=res)
-得到字典
ser.data就是序列化后的字典
serializer.py
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
# 要序列化哪个字段
id=serializers.IntegerField()
# id=serializers.CharField()
title=serializers.CharField(max_length=32)
price=serializers.DecimalField(max_digits=5, decimal_places=2)
publish=serializers.CharField(max_length=32)
models.py
class Book(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
publish = models.CharField(max_length=32)
views.py
from rest_framework.views import APIView
from app01 import models
from app01.serializer import BookSerializer
class Book(APIView):
def get(self,request,*args,**kwargs):
res=models.Book.objects.all()
# 借助序列化器
# 如果是多条,就是many=True
# 如果是单个对象,就不写
ser=BookSerializer(instance=res,many=True)
# 通过序列化器得到的字典
# ser.data
print(ser.data)
return Response(ser.data)
urls.py
path('books/', views.Book.as_view()),
# 字段类型(记列的这几个)
-IntegerField
-CharField
-DecimalField
-DateTimeField
-。。。跟models中大差不差
# 常用字段参数
-选项参数
max_length 最大长度
min_lenght 最小长度
allow_blank 是否允许为空
trim_whitespace 是否截断空白字符
max_value 最小值
min_value 最大值
-通用参数
#重点
read_only 表明该字段仅用于序列化输出,默认False,,posman中可见修改时可以不传参数
write_only 表明该字段仅用于反序列化输入,默认False,posman中不可见,修改时必须传参数
# 掌握
required 表明该字段在反序列化时必须输入,默认True
default 反序列化时使用的默认值
allow_null 表明该字段是否允许传入None,默认False
# 了解
validators 该字段使用的验证器
error_messages 包含错误编号与错误信息的字典
# 如果序列化类继承的是Serializer,必须重写create方法
# 使用方式
-视图类
def post(self, request):
print(request.data)
ser = BookSerializer(data=request.data)
if ser.is_valid(): # 校检数据
ser.save() # 保存到数据库中
return Response(ser.data)
else:
# 没有校验通过的错误信息
return Response(ser.errors)
-序列化类
class BookSerializer(serializers.Serializer):
...
def create(self, validated_data):
res=models.Book.objects.create(**validated_data)
print(res)
return res
# 三种方式
1-字段自己的校验规则(max_length...)
2-validators的校验
publish = serializers.CharField(max_length=32,validators=[check,])
def check(data):
if len(data)>10:
raise ValidationError('最长不能超过10')
else:
return data
3-局部和全局钩子
# 局部钩子,validate_字段名,需要带一个data,data就是该字段的数据
def validate_title(self, data):
if data.startswith('sb'):
raise ValidationError('不能以sb开头')
else:
return data
# 全局钩子
def validate(self, attrs):
title=attrs.get('title')
publish=attrs.get('publish')
if title==publish:
raise ValidationError('书名不能跟出版社同名')
else:
return attrs
read_only 表明该字段仅用于序列化输出,默认False
write_only 表明该字段仅用于反序列化输入,默认False
class BookSerializer(serializers.Serializer):
# 要序列化哪个字段
id = serializers.IntegerField(required=False)
# id=serializers.CharField()
title = serializers.CharField(max_length=32,min_length=2,read_only=True)
price = serializers.DecimalField(max_digits=5, decimal_places=2)
# 序列化的时候看不到
publish = serializers.CharField(max_length=32,validators=[check,],write_only=True)
from rest_framework.views import APIView
from rest_framework.response import Response
from app01 import models
from app01.serializer import BookSerializer
class Book(APIView):
def put(self, request, id):
# 通过id取到对象
res = {
'code': 100, 'msg': ''}
try:
book = models.Book.objects.get(id=id)
ser = BookSerializer(instance=book, data=request.data)
ser.is_valid(raise_exception=True)
ser.save() # 这里修改数据必须要重写BookSerializer中的update方法
res['msg'] = '修改成功'
res['result'] = ser.data
except Exception as e:
res['code'] = 101
res['msg'] = str(e)
return Response(res)
def delete(self,request,id):
response = {
'code': 100, 'msg': '删除成功'}
models.Book.objects.filter(id=id).delete()
return Response(response)
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
id = serializers.IntegerField(required=False)
title = serializers.CharField(max_length=32,min_length=2)
price = serializers.DecimalField(max_digits=5, decimal_places=2)
publish = serializers.CharField(max_length=32)
def create(self, validated_data):
res=models.Book.objects.create(**validated_data)
print(res)
return res
def update(self, book, validated_data):
book.title=validated_data.get('title')
book.price=validated_data.get('price')
book.publish=validated_data.get('publish')
book.save()
return book
models.py
class Book(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=32,null=True)
price = models.DecimalField(max_digits=5, decimal_places=2,null=True)
# publish = models.CharField(max_length=32)
publish = models.ForeignKey(to='Publish',null=True,on_delete=models.CASCADE)
def test(self):
# python是强类型语言,不支持字符串和数字直接相加
return self.title+str(self.price)
class Publish(models.Model):
name=models.CharField(max_length=32)
addr=models.CharField(max_length=32)
def __str__(self):
return self.name
serializer.py
class BookSerializer(serializers.Serializer):
id = serializers.IntegerField(required=False)
name = serializers.CharField(max_length=32,min_length=2,source='title')
price = serializers.DecimalField(max_digits=5, decimal_places=2)
publish = serializers.CharField(max_length=32,source='publish.name')
xxx=serializers.CharField(source='test')
publish_addr=serializers.CharField(source='publish.addr')
1 修改返回到前端的字段名
# source=title 字段名就不能再叫title
name = serializers.CharField(max_length=32,min_length=2,source='title')
2 如果表模型中有方法
# 执行表模型中的test方法,并且把返回值赋值给xxx
xxx=serializers.CharField(source='test')
3 sourc支持跨表操作
addr=serializers.CharField(source='publish.addr')
# 希望你们去看以下源码,内部如何实现的
def bind(self, field_name, parent):
"""
Initializes the field name and parent for the field instance.
Called when a field is added to the parent serializer instance.
"""
# In order to enforce a consistent style, we error if a redundant
# 'source' argument has been used. For example:
# my_field = serializer.CharField(source='my_field')
assert self.source != field_name, (
"It is redundant to specify `source='%s'` on field '%s' in "
"serializer '%s', because it is the same as the field name. "
"Remove the `source` keyword argument." %
(field_name, self.__class__.__name__, parent.__class__.__name__)
)
# self.source should default to being the same as the field name.
if self.source is None:
self.source = field_name
# self.source_attrs is a list of attributes that need to be looked up
# when serializing the instance, or populating the validated data.
if self.source == '*':
self.source_attrs = []
else:
self.source_attrs = self.source.split('.')
1 原来用的Serilizer跟表模型没有直接联系,模型类序列化器ModelSerilizer跟表模型有对应关系
2 使用
from rest_framework import serializers
class BookModelSerializer(serializers.ModelSerializer):
# 重写某些字段
publish = serializers.CharField(max_length=32,source='publish.name')
class Meta:
model=表模型 # 跟哪个表模型建立关系
fields=[字段,字段] # 序列化的字段,反序列化的字段
fields='__all__' # 所有字段都序列化,反序列化
exclude=[字段,字段] # 排除哪些字段(不能跟fields同时使用)
read_only_fields=['price','publish'] # 序列化显示的字段
#可能有的版本不能用
write_only_fields=['title'] # 反序列化需要传入的字段
# 给字段额外添加参数
extra_kwargs ={
'title':{
'max_length':32,'write_only':True}}
depth=1 # 了解,跨表1查询,最多建议写3
# 局部钩子,全局钩子,跟原来完全一样
3 新增,修改
-统统不用重写create和update方法了,在ModelSerializer中重写了create和update
class ModelSerializer(Serializer):
"""
* Default `.create()` and `.update()` implementations are provided.
"""
# Default `create` and `update` behavior...
def create(self, validated_data):
"""
this method is essentially just:
return ExampleModel.objects.create(**validated_data)
"""
raise_errors_on_nested_writes('create', self, validated_data)
ModelClass = self.Meta.model
# Remove many-to-many relationships from validated_data.
# They are not valid arguments to the default `.create()` method,
# as they require that the instance has already been saved.
info = model_meta.get_field_info(ModelClass)
many_to_many = {
}
for field_name, relation_info in info.relations.items():
if relation_info.to_many and (field_name in validated_data):
many_to_many[field_name] = validated_data.pop(field_name)
try:
instance = ModelClass._default_manager.create(**validated_data)
# Save many-to-many relationships after the instance is created.
if many_to_many:
for field_name, value in many_to_many.items():
field = getattr(instance, field_name)
field.set(value)
return instance
def update(self, instance, validated_data):
raise_errors_on_nested_writes('update', self, validated_data)
info = model_meta.get_field_info(instance)
# Simply set each attribute on the instance, and then save it.
# Note that unlike `.create()` we don't need to treat many-to-many
# relationships as being a special case. During updates we already
# have an instance pk for the relationships to be associated with.
m2m_fields = []
for attr, value in validated_data.items():
if attr in info.relations and info.relations[attr].to_many:
m2m_fields.append((attr, value))
else:
setattr(instance, attr, value)
instance.save()
# Note that many-to-many fields are set after updating instance.
# Setting m2m fields triggers signals which could potentially change
# updated instance and we do not want it to collide with .update()
for attr, value in m2m_fields:
field = getattr(instance, attr)
field.set(value)
return instance
models.py
class Book(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=32,null=True)
price = models.DecimalField(max_digits=5, decimal_places=2,null=True)
# publish = models.CharField(max_length=32)
publish = models.ForeignKey(to='Publish',null=True,on_delete=models.CASCADE)
class Publish(models.Model):
name=models.CharField(max_length=32)
addr=models.CharField(max_length=32)
def __str__(self):
return self.name
serializer.py
# 方式一:Serializer中
class BookSerializer(serializers.Serializer):
id = serializers.IntegerField(required=False)
name = serializers.CharField(max_length=32,min_length=2,source='title')
price = serializers.DecimalField(max_digits=5, decimal_places=2)
publish = serializers.SerializerMethodField()
def get_publish(self,obj):
dic={
'name':obj.publish.name,'addr':obj.publish.addr}
return dic
# 方式二:ModelSerializerr中
class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = '__all__'
publish = serializers.SerializerMethodField()
def get_publish(self,obj):
dic={
'name':obj.publish.name,'addr':obj.publish.addr}
return dic
# 方式三:使用序列化类的嵌套
class PublishModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Publish
# fields = '__all__'
fields = ['name','addr']
class BookModelSerializer(serializers.ModelSerializer):
publish = PublishModelSerializer()
class Meta:
model = models.Book
fields = '__all__'
# Request
-data :前端以post请求提交的数据都在它中:urlencoded\form-data\json
-FILES :前端提交的文件
-query_params:就是原来的request.GET
-重写了 __getattr__
-使用新的request.method其实取得就是原生request.method(通过反射实现)
# Response
class Response(SimpleTemplateResponse):
def __init__(self, data=None, status=None,
template_name=None, headers=None,
exception=False, content_type=None):
-from rest_framework.response import Response
-对象.data:响应的字典
-status:http响应的状态码
-drf提供给你了所有的状态码,以及它的意思
from rest_framework.status import HTTP_201_CREATED
-template_name:模板名字(一般不动),了解
-headers:响应头,字典
-content_type:响应的编码方式,了解
# 自己封装一个Response对象
class CommonResponse:
def __init__(self):
self.code=100
self.msg=''
@property
def get_dic(self):
return self.__dict__
# 自己封装一个response,继承drf的Response
# 通过配置,选择默认模板的显示形式(浏览器方式,json方式)
-配置文件方式(全局)
-如果没有配置,默认有浏览器和json
-drf有默认配置文件
from rest_framework.settings import DEFAULTS
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': ( # 默认响应渲染类
'rest_framework.renderers.JSONRenderer', # json渲染器
'rest_framework.renderers.BrowsableAPIRenderer', # 浏览API渲染器
)
}
-在视图类中配置(局部)
-粒度更小
-class BookDetail(APIView):
renderer_classes = [JSONRenderer,]
1 many=True
-__init__----->一路找到了BaseSerializer---》__new__决定了生成的对象是谁
class BaseSerializer(Field):
def __new__(cls, *args, **kwargs):
# We override this method in order to automatically create
# `ListSerializer` classes instead when `many=True` is set.
if kwargs.pop('many', False):
return cls.many_init(*args, **kwargs)
return super().__new__(cls, *args, **kwargs)
@classmethod
def many_init(cls, *args, **kwargs):
allow_empty = kwargs.pop('allow_empty', None)
child_serializer = cls(*args, **kwargs)
list_kwargs = {
'child': child_serializer,
}
if allow_empty is not None:
list_kwargs['allow_empty'] = allow_empty
list_kwargs.update({
key: value for key, value in kwargs.items()
if key in LIST_SERIALIZER_KWARGS
})
meta = getattr(cls, 'Meta', None)
list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer)
return list_serializer_class(*args, **list_kwargs)
2 入口是is_valid()---》BaseSerializer--》is_valid---》self._validated_data = self.run_validation(self.initial_data)
-Serializer这个类的:self.run_validation
def run_validation(self, data=empty):
value = self.to_internal_value(data) # 局部字段自己的校验和局部钩子校验
try:
self.run_validators(value)
value = self.validate(value) # 全局钩子的校验
except (ValidationError, DjangoValidationError) as exc:
raise ValidationError(detail=as_serializer_error(exc))
return value
# Django REST framwork 提供的视图的主要作用:
控制序列化器的执行(检验、保存、转换数据)
控制数据库查询的执行
# APIView:继承了原生Django的View
1、models..py
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.CharField(max_length=32)
publish = models.CharField(max_length=32)
2、bookserializer.py
from rest_framework import serializers
from app01 import models
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = '__all__'
3、views.py
from django.shortcuts import render
from app01 import models, bookser
from rest_framework.views import APIView
from rest_framework.response import Response
class BookSerializer(APIView):
def get(self, request):
book_list = models.Book.objects.all()
ser = bookser.BookSerializer(instance=book_list, many=True)
return Response(ser.data)
def post(self, request):
ser = bookser.BookSerializer(data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
else:
return Response(ser.errors)
class BookDetailSerializer(APIView):
def get(self, request, pk):
book = models.Book.objects.filter(pk=pk).first()
ser = bookser.BookSerializer(instance=book)
return Response(ser.data)
def put(self, request, pk):
book = models.Book.objects.filter(pk=pk).first()
ser = bookser.BookSerializer(instance=book, data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
else:
return Response(ser.errors)
def delete(self, request, pk):
models.Book.objects.filter(pk=pk).delete()
return Response('删除成功')
4、urls.py
from django.contrib import admin
from django.urls import path, re_path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', views.BookSerializer.as_view()),
re_path('books/(?P\d+)' , views.BookDetailSerializer.as_view()),
]
5、settings.py
INSTALLED_APPS = [
'rest_framework'
]
# GenericAPIView:继承了APIView
-queryset = models.Book.objects.all()
-serializer_class = serializer.BookModelSerializer
-get_queryset:获取配置的queryset
-get_object:路由中的分组字段必须是pk
-get_serializer:获取配置的序列化类
1、bookserizlizer.py
from app01 import models, bookser
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
class BookGenericAPIView(GenericAPIView):
queryset = models.Book.objects.all()
serializer_class = bookser.BookSerializer
def get(self, request, *args, **kwargs):
book_list = self.get_queryset()
ser = self.serializer_class(instance=book_list, many=True)
return Response(ser.data)
def post(self, request, *args, **kwargs):
ser = self.serializer_class(data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
else:
return Response(ser.errors)
class BookDetailGenericAPIView(GenericAPIView):
queryset = models.Book.objects.all()
serializer_class = bookser.BookSerializer
def get(self, request, *args, **kwargs):
book = self.get_object()
ser = self.serializer_class(instance=book)
return Response(ser.data)
def put(self, request, *args, **kwargs):
book = self.get_object()
ser = self.serializer_class(instance=book, data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
else:
return Response(ser.errors)
def delete(self, request, *args, **kwargs):
models.Book.objects.filter(pk=kwargs.get('pk')).delete()
return Response('删除成功')
2、urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', views.BookGenericAPIView.as_view()),
re_path('books/(?P\d+)' , views.BookDetailGenericAPIView.as_view()),
]
# 最终总结
#两个基类
APIView:如果跟models没有关系(没有数据库相关操作),就继承它
GenericAPIView:有关数据库操作,queryset 和serializer_class
#5个视图扩展类(rest_framework.mixins)
CreateModelMixin: create方法创建一条
DestroyModelMixin:destory方法删除一条
ListModelMixin:list方法获取所有
RetrieveModelMixin:retrieve获取一条
UpdateModelMixin:update修改一条
1、bookserializer.py
from app01 import models, bookser
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin, CreateModelMixin, DestroyModelMixin, RetrieveModelMixin, UpdateModelMixin
class BookMixin(GenericAPIView, ListModelMixin, CreateModelMixin):
queryset = models.Book.objects.all()
serializer_class = bookser.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 BookDetailMixin(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
queryset = models.Book.objects.all()
serializer_class = bookser.BookSerializer
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
2、urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', views.BookMixin.as_view()),
re_path('books/(?P\d+)' , views.BookDetailMixin.as_view()),
]
|| 9个子类视图(rest_framework.generics) | 继承 | 方法 |
| :----------------------------------- | ------------------------------------------------------------ | ----------------------------------------- |
| CreateAPIView | CreateModelMixin,GenericAPIView | 有post方法,新增数据 |
| DestroyAPIView | CreateModelMixin,GenericAPIView | 有delete方法,删除数据 |
| ListAPIView | ListModelMixin,GenericAPIView | 有get方法获取所有 |
| UpdateAPIView | UpdateModelMixin,GenericAPIView | 有put和patch方法,修改单个数据 |
| RetrieveAPIView | RetrieveModelMixin,GenericAPIView | 有get方法,获取一条 |
| ListCreateAPIView | ListModelMixin,CreateModelMixin,GenericAPIView | 有get获取所有,post方法新增 |
| RetrieveDestroyAPIView | RetrieveModelMixin,DestroyModelMixin,GenericAPIView | 有get方法获取一条,delete方法删除 |
| RetrieveUpdateAPIView | RetrieveModelMixin,UpdateModelMixin,GenericAPIView | 有get获取一条,put,patch修改 |
| RetrieveUpdateDestroyAPIView | RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin,GenericAPIView | 有get获取一条,put,patch修改,delete删除 || |
|–|--|
| | |
1、bookserializer.py
from app01 import models, bookser
from rest_framework.generics import ListAPIView, CreateAPIView, RetrieveAPIView, UpdateAPIView, DestroyAPIView, ListCreateAPIView, RetrieveUpdateAPIView, RetrieveDestroyAPIView, RetrieveUpdateDestroyAPIView
class BookListCreateAPIView(ListCreateAPIView):
queryset = models.Book.objects.all()
serializer_class = bookser.BookSerializer
class BookDetaiRetrieveUpdateDestroyAPIView(RetrieveUpdateDestroyAPIView):
queryset = models.Book.objects.all()
serializer_class = bookser.BookSerializer
2、urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', views.BookListCreateAPIView .as_view()),
re_path('books/(?P\d+)' , views.BookDetaiRetrieveUpdateDestroyAPIView.as_view()),
]
ViewSetMixin
APIView
1、ser.py
from rest_framework.viewsets import ViewSet
class WuView(ViewSet):
def wuxi(self, request,*args,**kwargs):
return Response('wuxi')
2、urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('wuxis/', views.WuView .as_view({
'get' 'wuxi'})),
]
ViewSetMixin,
generics.GenericAPIView
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet
1、bookserializer.py
from app01 import models, bookser
from rest_framework.viewsets import ModelViewSet
class BookListModelViewSet(ModelViewSet):
queryset = models.Book.objects.all()
serializer_class = bookser.BookSerializer
2、urls.py
urlpatterns = [
path('books/', views.BookListModelViewSet.as_view({
'get': 'list', 'post': 'create'})),
re_path('books/(?P\d+)' , views.BookListModelViewSet.as_view({
'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
]
mixins.RetrieveModelMixin,
mixins.ListModelMixin,GenericViewSet
1、bookserializer.py
from rest_framework.viewsets import ReadOnlyModelViewSet
class BookListReadOnly(ReadOnlyModelViewSet):
queryset = models.Book.objects.all()
serializer_class = bookser.BookSerializer
2.urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', views.BookListReadOnly.as_view({
'get': 'list'})),
re_path('books/(?P\d+)' , views.BookListReadOnly.as_view({
'get': 'retrieve'})),
]
1、ViewSet的映射关系
![1604654204669](1604654204669.png)
2、ReadOnlyModelViewSet只能查询,不能新增、修改、删除
![1604655009360](1604655009360.png)
## 5、action的使用
```python
# 只要继承了ViewSetMixin类
# 路由配置:path('books_mix/', views.BookView.as_view({'get':'lqz'}))
# 视图类的方法中就会有个action
class BookView(ViewSet):
def lqz(self,request,*args,**kwargs):
print(self.action)
return Response('lqz')
# ViewSetMixin以后只要继承它,路由的配置就发生变化了,只需要写映射即可
# 自动生成路由
# SimpleRouter
# DefaultRouter
# 继承了ViewSetMixin的视图类,以后写路由,可以自动生成
from rest_framework import routers
# 实例化得到一个对象
router = routers.SimpleRouter()
# 注册进路由
router.register('books', views.BookSetView)
# 把自动生成的路由配置到urlpatterns中
-urlpatterns += router.urls
-re_path(r'v1/', include(router.urls))
1.urls.py
from rest_framework import routers
router = routers.SimpleRouter()
router.register('books', views.BookModelViewSet, basename='book')
urlpatterns = [
path('admin/', admin.site.urls),
]
urlpatterns += router.urls
print(router.urls)
2、bookserializer.py
from rest_framework.viewsets import ReadOnlyModelViewSet
class BookListReadOnly(ReadOnlyModelViewSet):
queryset = models.Book.objects.all()
serializer_class = bookser.BookSerializer
=========================================================================================
[<URLPattern '^books/$' [name='book-list']>,
<URLPattern '^books/(?P[^/.]+)/$' [name='book-detail']>]
# 配置路由的方式
-最原始的
-path('books/', views.BookAPIView.as_view()),
-ViewSetMixin的视图类
-path('books_set/', views.BookSetView.as_view({
'get':'list','post':'create'}))
-ViewSetMixin的视图类
-自动生成,上面讲的
# action
-当自动生成路由的时候,由于视图类中还有其它方法,是无法自动生成路由的
-加action装饰器:
-methods:什么请求方式会触发被装饰函数的执行
-detail:True是基于带id的路由生成的,如果是False,是基于不带id的路由生成的
-@action(methods=['get'], detail=True)
1、bookserializer.py
from rest_framework.decorators import action
class BookModelViewSet(ModelViewSet):
queryset = models.Book.objects.all()
serializer_class = bookser.BookSerializer
@action(methods=['get'], detail=False) # 基于不带id的路由生成
def login(self, request):
return Response('login')
2、urls.py
from rest_framework import routers
router = routers.SimpleRouter()
router.register('books', views.BookModelViewSet, basename='book')
urlpatterns = [
path('admin/', admin.site.urls),
]
urlpatterns += router.urls
print(router.urls)
[<URLPattern '^books/$' [name='book-list']>,
<URLPattern '^books/login/$' [name='book-login']>, # 基于不带id的路由生成
<URLPattern '^books/(?P[^/.]+)/$' [name='book-detail']>]
3、bookserializer.py
from rest_framework.decorators import action
class BookModelViewSet(ModelViewSet):
queryset = models.Book.objects.all()
serializer_class = bookser.BookSerializer
@action(methods=['get'], detail=True) # 基于带id的路由生成
def login(self, request, pk): # 注意这里需要参数
return Response('login')
4、urls.py
from rest_framework import routers
router = routers.SimpleRouter()
router.register('books', views.BookModelViewSet, basename='book')
urlpatterns = [
path('admin/', admin.site.urls),
]
urlpatterns += router.urls
print(router.urls)
[<URLPattern '^books/$' [name='book-list']>,
<URLPattern '^books/(?P[^/.]+)/$' [name='book-detail']>,
<URLPattern '^books/(?P[^/.]+)/login/$' [name='book-login']>] # 基于带id的路由生成
1 APIView---》dispatch---》
self.initial(request, *args, **kwargs)--》self.perform_authentication(request)
---》Request.user--->self._authenticate(self):Request类的方法
---》self.authenticators:Request类的属性---》在Request对象实例化的时候传入的----》
Request在什么时候实例化的?dispatch的时候---》APIView:self.get_authenticators()--》
return [auth() for auth in self.authentication_classes]----》
如果在自己定义的视图类中写了authentication_classes=[类1,类2]----》
Request的self.authenticators就变成了我们配置的一个个类的对象
2 self._authenticate(self):Request类的方法
def _authenticate(self):
for authenticator in self.authenticators: # BookView中配置的一个个类的对象
try:
user_auth_tuple = authenticator.authenticate(self)
except exceptions.APIException:
self._not_authenticated()
raise
if user_auth_tuple is not None:
self._authenticator = authenticator
self.user, self.auth = user_auth_tuple
return
3 只要在视图类中配置authentication_classes = [MyAuthen.LoginAuth, ]
就会执行上面的方法,执行认证
1 使用
-定义一个类,继承BaseAuthentication
models.py
class User(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
user_type = models.IntegerField(choices=((1, '超级用户'), (2, '普通用户'), (3, '二笔用户')),default=1)
class UserToken(models.Model):
user = models.OneToOneField(to='User',on_delete=models.CASCADE)
token = models.CharField(max_length=64)
AppAuthentication.py
from app01 import models
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.authentication import BaseAuthentication
class LoginAuth(BaseAuthentication):
def authenticate(self, request):
# 写认证规则,如果过认证通过,就正常return
# 如果认证失败,抛异常
# 取出携带的随机字符串,去数据判断,如果有正常登录,没有,告诉它没有登录
# 在get请求的参数中,放到请求头中
token = request.GET.get('token')
res = models.UserToken.objects.filter(token=token).first()
if res:
return (res.user, token)
else:
raise AuthenticationFailed('您没有登录')
Views.py
class LoginView(APIView):
authentication_classes = [] # 局部禁用
def post(self, request):
res = {
'code': 100, 'msg': '登录成功'}
username = request.data.get('username')
password = request.data.get('password')
# 查询数据库
user = models.User.objects.filter(username=username, password=password).first()
if user:
token = uuid.uuid4() # 生成一个随机字符串
# 把token存到UserToken表中(如果是第一次登录:新增,如果不是第一次:更新)
models.UserToken.objects.update_or_create(defaults={
'token': token}, user=user)
res['token'] = token
return Response(res)
else:
res['code'] = 101
res['msg'] = '用户名或密码错误'
return Response(res)
-重写authenticate方法
-局部使用和全局使用
-局部:在视图类中配置(只要配置了,就是登录以后才能访问,没配置,不用登录就能访问)
authentication_classes = [MyAuthen.LoginAuth, ]
-全局
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": ["app01.MyAuthen.LoginAuth", ]
}
-注意:
1 认证类,认证通过可以返回一个元组,有两个值,第一个值会给,request.user,第二个值会个request.auth
2 认证类可以配置多个,按照从前向后的顺序执行,如果前面有返回值,认证就不再继续往下走了
1 登录成功以后,超级用户可以干某些事,普通用户不能干---》超级用户可以查看某些接口,普通用户不能查看
2 使用写一个类继承BasePermission,重写has_permission
class SuperPermission(BasePermission):
def has_permission(self, request, view):
# Return `True` if permission is granted, `False` otherwise.
# 超级用户可以访问,除了超级用户以外,都不能访问
if request.user.user_type == '1':
return True
else:
return False
3 局部使用和全局使用
-在想局部使用的视图类上
permission_classes = [MyAuthen.SuperPermission]
-全局使用
REST_FRAMEWORK = {
"DEFAULT_PERMISSION_CLASSES": ["app01.MyAuthen.SuperPermission", ]
}
-局部禁用
permission_classes = []
APIView---》dispatch---》self.initial(request, *args, **kwargs)---》self.check_permissions(request)
---》self.get_permissions()
def check_permissions(self, request):
"""
Check if the request should be permitted.
Raises an appropriate exception if the request is not permitted.
"""
for permission in self.get_permissions():
if not permission.has_permission(request, self):
self.permission_denied(
request,
message=getattr(permission, 'message', None),
code=getattr(permission, 'code', None)
) def check_permissions(self, request):
def get_permissions(self):
"""
Instantiates and returns the list of permissions that this view requires.
"""
return [permission() for permission in self.permission_classes]
message = {
"detail": '您的权限不够'}