一、九个视图子类
不用自己再写get,post,put等方法,视图子类给我们封装了
from rest_framework.generics import GenericAPIView, ListCreateAPIView, RetrieveUpdateDestroyAPIView, ListAPIView, \
UpdateAPIView, DestroyAPIView, RetrieveAPIView, CreateAPIView,RetrieveDestroyAPIView
九个视图子类继承了GenericAPIView,所以写类时可以不用写GenericAPIView
## 路由
urlpatterns = [
path('books/', views.BookView.as_view()),
path('books/<int:pk>/', views.BookView.as_view()),
]
# 视图类
class BookView(ListCreateAPIView): # 查询所有,新增一个
queryset = Book.objects.all()
serializer_class = BookSerializer
# 新增一个,修改一个,删除一个
class BookDetailView(RetrieveUpdateDestroyAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
二、ModelViewSet(重点)
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
发现ModelViewSet继承了五个学过的视图类
继承ModelViewSet后,五个接口都写好了,但是路由需要配置一下
因为ModelViewSet里面的ViewSetMixin重写了as_view方法,并且该父类写在另一个父类的左边,所以就先执行ViewSetMixin的as_view了
路由中需要给as_view()配置actions={},用来控制执行的方法,不然就会报异常
但是又发现如果只开一个路由,get有两个冲突,再重新开一条路由,调用同一个视图类
先导一下
from rest_framework.viewsets import ModelViewSet
class BookView(ModelViewSet):
queryset = Book.object
serializer_class = BookSerializers
#路由层
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', views.BooksView.as_view(actions={'get': 'list', 'post': 'create'})),
# path('books/<int:pk>/',views.BookDetailView.as_view()),
path('books/<int:pk>/', views.BooksView.as_view(actions={'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}))
]
1.裂变,只读,两个接口的
导入一下
from rest_framework.viewsets import ReadOnlyModelViewSet
只读————ReadOnlyModelViewSet
2.ViewSetMixin高级用法(高级,重要)
只要继承了ViewSetMixin,并且必须写在APIVIEW或者GenericAPIView前面!!!
就可以根据自己需要,改写list等方法,可以任意命名,因为反正路由那边是根据K:V的形式来做映射
可以基于ViewSetMixin玩一玩,这个就只是改写as_view方法达到映射,比如我们搭配APIVIEW来玩玩,配一下get对应的方法即可
from rest_framework.viewsets import ViewSetMixin
class TestView1(ViewSetMixin, APIView):# 也等于ViewSet
def hahaha(self, request):
print('你最好小心一点')
return Response('你最好小心一点')
3.重点
from rest_framework.viewsets import ViewSet,GenericViewSet
总结:
ViewSetMixin + APIVIEW = ViewSet
ViewSetMixin + GenericAPIView = GenericViewSet
"""
以后,你想继承APIView,但是想变路由写法【视图类中方法名任意命名】,要继承ViewSet
以后,你想继承GenericAPIView,但是想变路由写法【视图类中方法名任意命名】,要继承GenericViewSet
"""
三、视图类大总结!
1.两个视图基类
-APIView,GenericAPIView
2.五个视图拓展类,必须配合GenericAPIView
这个用不上了,已经有九个视图子类能完美替代
3.9个视图子类
4.视图集
-ModelViewSet:路由写法变了,只需要写两行,5个接口都有了
-ReadOnlyModelViewSet:路由写法变了,只需要写两行,2个只读接口都有了
-ViewSetMixin:不是视图类,魔法,重写了as_view,路由写法变了,变成映射了
-ViewSet:ViewSetMixin+ APIView
-GenericViewSet:ViewSetMixin+ GenericAPIView
总结:
1.小功能不和数据库打交道可以ViewSet,然后自己写见名只知意的方法
和数据库打交道可以GenericViewSet
因为用GenericViewSet必须配置那个表,就相当于查了一遍表,占用资源
2.如果要写一个视图类只要一个或一两个方法,直接继承9个视图子类里面的
四、路由系统
能自动生成路由的方法
视图层类要继承ModelViewSet
1.然后路由层,导入路由类
.routers import SimpleRouter # 这个生成两个路由,常用
.routers import DefaultRouter # 这个生成的路由好多个,不太常用
2.实例化一个路由器对象
router = SimpleRouter()
3.注册,让路由对应视图类,有几个视图类就要写几次
router.register('books',views.BookView)
这个括号内还可以写第三个参数,类似反向解析,直接写就行不用写name=啥
4.填入路由层的匹配列表
有两种方式,第二种用的多一些
urlpatterns += router.urls
# include 需要导入一下,from django.urls import path,include
path('api/v1/',include(router.urls))
5.注意使用第二种方法后,要打全api/v1/才能进去根页面
DefaultRouter和根
DefaultRouter能生成很多个路由,并且在你的api的根首页,可以看到你整体api的地址
总结
本质是自动做映射,能够自动成的前提是,视图类中要有 5个方法的某要给或多个
1.ModelViewSet,ReadOnlyModelViewSet可以自动生成
2.9个视图子类+配合ViewSetMixin 才可以自动生成
比如
class PublishView(ViewSetMixin,ListAPIView):
queryset = Publish.objects
serializer_class = PublishSerializers
3.GenericAPIView+5个试图扩展类+配合ViewSetMixin 才能自动生成
五、actions装饰器
如果想写一个或者两个的普通视图类,用ViewSetMixin做映射,写点方法,发现正常写路由层的actions={}可以实现,但是想偷懒,想后期都不在动路由层的urlpatterns,这时候就可以用到装饰器
1.导一下
from rest_framework.decorators import action
2.我们学习一下action的参数
methods=[] 是指定使用这个装饰器的方法映射原来的哪个属性
detail=True/False
False是不带ID(PK之类的)的路径
send/send_msg
True是带ID的路径,写了后一定要给被装饰函数增加形参
send/pk/send_msg
url_path:生成send后路径的名字,默认以方法名命名
url_name:别名,反向解析使用,了解即可
class SendView(ViewSet):
@action(methods=['GET'], detail=False)
def send_msg(self, request):
name = request.query_params.get('name')
phone = request.query_params.get('phone')
return Response({'code': 100, 'msg': [name,phone]})
# 补充:
-1 不同请求方式可以使用不同序列化类
-2 不同action使用不同序列化类
class SendView(GenericViewSet):
queryset = None
serializer_class = '序列化类'
def get_serializer(self, *args, **kwargs):
if self.action=='lqz':
return '某个序列化类'
else:
return '另一个序列化列'
@action(methods=['GET'], detail=True)
def send_sms(self, request,pk):
print(pk)
# 手机号,从哪去,假设get请求,携带了参数
phone = request.query_params.get('phone')
print('发送成功,%s' % phone)
return Response({'code': 100, 'msg': '发送成功'})
@action(methods=['GET'], detail=True)
def lqz(self,request): # get
# 序列化类
pass
@action(methods=['GET'], detail=True)
def login(self,request): # get
# 序列化类
pass
六、认证组件
前戏
写一个登录功能接口
1.写呗,涉及数据,先建立表
class User(models.Model):
name = models.CharField(max_length=32)
password = models.CharField(max_length=32)
class UserToken(models.Model):
token = models.CharField(max_length=64)
user = models.OneToOneField(to='User',on_delete=models.CASCADE,null=True)
2.写视图类
class UserView(ViewSet):
@action(methods=['POST'], detail=False)
def login(self, request):
username = request.data.get('name')
password = request.data.get('password')
user_obj = User.objects.filter(name=username, password=password).first()
if user_obj:
res = str(uuid.uuid4())
obj = UserToken.objects.filter(user=user_obj)
if obj:
UserToken.objects.update(user=user_obj,token=res)
return Response({'code': 100, 'msg': '登录成功,欢迎回来'})
else:
UserToken.objects.create(user=user_obj,token=res)
return Response({'code': 100, 'msg': '登录成功'})
# 高级写法,不用if update_or_create 如果有就修改,如果没有就新增
# kwargs 传入的东西查找,能找到,使用defaults的更新,否则新增一条
# UserToken.objects.update_or_create(user=user_obj, defaults={'token': res})
else:
return Response({'code': 101, 'msg': '登录失败'})
3.写路由
router.register('user',views.UserView,'login')
4.POSTMAN验证,地址写对啊!
http://127.0.0.1:8000/api/v1/user/login/,然后传数据到后端
今日单词
generics 泛型 放GenericAPIView和九个视图子类的地方
Router 路由器
ViewSetMixin 来自viewsets,用来映射路由
今日注意
1.注意使用路由自动添加第二种方法include后,要打全api/v1/才能进去根页面
2.request.query_params是request.GET的一个更准确的同义词。包含了所有用过get方式请求的参数,用来取字符串,携带在网址?后面的东西
3.注册路由时router.register('send', views.SendView,basename='send') 第三个参数带上最好,不然可能会报错
4.不同action使用不同序列化类不是很能理解
今日补充
1.on_delete=models. 级联更新级联删除这里,还有SET_NULL SET_DEFAULT
SET(函数内存地址)
因为不可能一个作者被删除了,所有书都被删除了
2.uuid模块,能生成uuid4()对象,是一个随机字符串,记得转str
3.update_or_create 如果有就修改,如果没有就新增
作业
class UserView(ViewSet):
@action(methods=['POST'], detail=False)
def login(self, request):
username = request.data.get('name')
password = request.data.get('password')
user_obj = User.objects.filter(name=username, password=password).first()
if user_obj:
res = str(uuid.uuid4())
obj = UserToken.objects.filter(user=user_obj)
# user_token_obj = UserToken.objects.create(user=user_obj, token=res)
if obj:
UserToken.objects.update(user=user_obj,token=res)
return Response({'code': 100, 'msg': '登录成功,欢迎回来'})
else:
UserToken.objects.create(user=user_obj,token=res)
return Response({'code': 100, 'msg': '登录成功'})
else:
return Response({'code': 101, 'msg': '登录失败'})
分类: drf项目