前5天内容小回顾
一、drf入门
- 亲后端开发模式
分离:drf方便我们快速写出接口
混合:bbs、模板语法
- API接口:前后端交互的媒介
- 接口测试工具:postman(不止这一款)
- restful规范
- 数据的安全保障:url链接一般都猜http协议进行传输 为了保证数据安全使用https协议传输 https指的是: http + ssl/tsl
- 接口中带api关键词
- 多数据版本共存,一个接口可能有多个版本 url连接中标识版本标识 eg:假设有个登录接口:1.0只要用户名、密码 2.0要用户名、密码、验证码
- 数据即是资源,均使用名词(可用复数)---接口尽量使用名词 -都是用名词,没法区分是增加,删除,修改,查询
- 资源操作由请求方式决定
- url地址中带过滤参数
- 响应带状态码
- 返回错误信息--->响应体中带错误信息 {code:100,msg:成功}
- 返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范
- 响应中带链接
- drf快速入门
- 必须继承APIView写视图类
View的执行流程:cbv的执行流程
-路由中:视图类.as_view()——本质是View的as_view内部的view闭包函数——dispatch——根据请求方式执行视图函数中得方法
APIView的执行流程
-as_view——本质是APIView的as_view:去掉csrf,调用父类的as_view
-请求来了执行的是去掉了csrf认证后的View的as_view内的view闭包函数
-self.dispatch——APIView
-包装了新的request
-执行三大认证
-处理全局异常
新的Request
-包装了老的request request._request
-为了让咱们使用起来习惯一样:重写了__getattr__
-data
-query_params
-文件,其他.......
二、序列化组件
是怎么帮助我们快速实现【序列化】、【反序列化】、【数据校验】
如何定义:
- 写一个序列化类继承(Serializer,ModelSerializer)
- 写要序列化的字段(字段类、字段参数:read_only,write_only)
- 字段自己的规则,局部钩子,全局钩子
- 序列化修改字段模式:
-序列化类中写SerializerMethodField,get_字段名
-表模型中写方法(返回字典,列表)
-{“name”:"curry","age":33}——如果它是字符串 json格式字符串
-{“name”:"curry","age":33}——如果它是字典 正常字典使用
-{“name”:"curry","age":33}——如果它是字典 正常字典使用
-{“name”:"curry","age":33}——如果它是字符串 不是json格式,json识别不了
在视图类中使用
-ser=BookSerializer(instance=要序列化的对象,many=True)
-ser=BookSerializer(data=request.data)
-ser=BookSerializer(instance=要修改的对象,data=request.data)
-ser.data 字典格式
-校验数据, ser.errors
-ser.save() 通过判断instance是否为空,决定调用的是create还是update
-如果继承了Serializer,必须重写create和upate
继承ModelSerializer写序列化类
class meta:
model=表模型
fields='__all__' 【一定要注册一下 】
extra_kwargs={'name':{read_only:True}}
重写某个字段
局部钩子全局钩子
序列化修改字段格式跟之前一样
一般不需要重写create和update
一次性提交的数据可能涉及到多个表时
注意:直接从表模型中映射的字段也有字段类和字段属性
三、请求与响应
请求对象、响应对象(Request,Response)
原生django响应头写内容
drf能够解析的请求格式:局部配置和全局配置
drf的响应格式:局部配置和全局配置
四、视图类
-两个视图基类
-APIView
-GenericAPIView:属性或方法
queryser = None
serializer_class = None
get_queryset()
get_object()
get_serializer()
5个视图扩展类:需要配合GenericAPIView用
9个视图子类:视图类,继承了GenericAPIView+5个视图扩展类之几+写个get、post、delete、put
视图集
-ViewSetMixin:重写了as_view,一旦继承了它及其子类,路由写法变了,as_view中加字典做映射
-ViewSet=ViewSetMixin+APIView
class TestView(ViewSet):
def login(self,request):
-GenericViewSet=iewSetMixin+GenericAPIView
class TestView(GenercViewSet):
queryset = None
serializer_class = None
def login(self,request)
-ModelViewSet:路由写法变了,两个类属性queryset,serializer_class
-ReadOnlyModelViewSet:路由写法变了,两个类属性queryset,serializer_class
以后到底要继承谁写视图类?
-假设写获取所有图书
-APIView
-GenericAPIView
-ListAPIView
五、路由
只有继承了ViewSetMixin及其子类的视图类
urls
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', views.BookView.as_view({'get': 'list', 'post': 'create'})),
path('books/<int:pk>', views.BookView.as_view({'get': 'list', 'post': 'create'})),
]
models
from django.db import models
# Create your models here.
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.CharField(max_length=32)
publish = models.CharField(max_length=32)
class Publish(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=32)
# 用户表
class User(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
def __str__(self):
return self.username
views
class BookView(ModelViewSet):
serializer_class = BookSerializer
queryset = Book.objects.all()
第二种方式自动生成路由
"""def_7 URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from app01 import views
# 自动生成路由
# drf提供了两个路由类,以后继承了ViewSetMixin及其子类的视图类,就可以使用这两个路由类来自动生成路由
# 使用步骤
# 第一步:导入路由类
from rest_framework.routers import SimpleRouter, DefaultRouter
# 第二步:实例化得到对象
router = SimpleRouter() # 自动生成的路由后面要不要带/ 默认是带/
# 第三步:注册路由(可以注册多个)
# 第一个参数:路径 第二个参数:视图类 第三个参数:别名 可以不写
router.register('books', views.BookView, 'books')
urlpatterns = [
path('admin/', admin.site.urls),
path('', include(router.urls)),
# path('books/', views.BookView.as_view({'get': 'list', 'post': 'create'})),
# path('books/<int:pk>', views.BookView.as_view({'get': 'list', 'post': 'create'})),
]
# 第四步:把自动生成的路由器添加到urlpatterns中
# 第四步: 方式二、利用include添加path(‘’,include(router。urls)),
# urlpatterns += router.urls
DefaultRouter能访问根路径
SimpleRouter不能访问根路径
小总结:
只要是继承ViewSetMixin及其子类的视图后 路由写法可以自动生成
第一步:导入路由类 from rest_framework.routers import SimpleRouter, DefaultRouter
第二步:实例化得到对象 router = SimpleRouter()
第三步:注册路由(可以注册多个) router.register('user',views.UserView,'user')
第四步:把自动生成的路由器添加到urlpatterns中 (添加进去有两种方式,上面代码写的)
自动生成的路由映射关系其实定死了()
/books/--->get--->list
/books/--->post--->create
/books/1--->get--->retrieve
以后写的视图类不需要写action装饰器的话,视图类中必须要有
-list,destroy,retrieve,create,update方法之一
-其实是必须是 5个视图扩展类之一+GenericAPIView 9个视图子类,ModelViewSet
action装饰器的使用
在视图函数中,会有一些其它名字的方法,必须要使用action装饰器做映射
methods:支持的请求方式,列表
detail:默认是False 控制生成的路由是 /user/login/ 还是 /user/pk/login 是不是带pk
url_path: 控制生成的/user/后的路径是什么,如果不写,默认以方法名命名 /user/login/,一般跟函数名同名即可
url_name:别名,用于反向解析
@action(methods=['GET','POST'], detail=True, url_path='login')
def login(self, request,pk):
# 这样写了以后可以:自动生成路由
六、登录接口编写
用户表
class User(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
def __str__(self):
return self.username
用户登录记录表
如何区分用户是否登录了?
class UserToken(models.Model):
# SET_NULL SET_DEFAULT CASCADE SET(函数内存地址)
user = models.OneToOneField(to='User', on_delete=models.CASCADE)
token = models.CharField(max_length=32, null=True) # 用户如果没有登录,就是空,如果登录了,就有值,登录多次以最后一次为准
views
class UserView(ViewSet):
authentication_classes = []
@action(methods=['POST', ], detail=False, url_path='login')
def login(self, request):
# 取出前端传入的用户名密码,校验,通过,返回登录成功,失败就返回用户名密码错误
username = request.data.get('username')
password = request.data.get('password')
user = User.objects.filter(username=username, password=password).first()
if user:
# 登录成功,不同人生成的token是不一样的,谁登录的,就把token存到UserToken表中
token = str(uuid.uuid4()) # 生成一个永不重复的随机字符串
# 存UserToken:如果没有记录,就是新增,如果有记录更新一下即可
# 通过user去UserToken表中查数据,如果能查到,使用defaults的数据更新,如果查不到,直接通过user和defaults的数据新增
UserToken.objects.update_or_create(defaults={'token': token}, user=user)
return Response({'code': 100, 'msg': '登录成功', 'token': token})
else:
return Response({'code': 101, 'msg': '用户名或密码错误'})
urls.py
from rest_framework.routers import SimpleRouter, DefaultRouter
router = SimpleRouter()
router.register('user',views.UserView,'user')
urlpatterns = [
path('admin/', admin.site.urls),
path('', include(router.urls)),
]
七、认证
访问接口,必须登录后才能访问
通过认证类完成,使用步骤
1 写一个认证类,继承BaseAuthentication
2 重写authenticate方法,在内部做认证
3 如果认证通过,返回2个值
4 认证不通过抛AuthenticationFailed异常
5 只要返回了两个值,在后续的request.user 就是当前登录用户
6 如果想让某个视图类登录后才能访问
-方式一:
class BookView(ModelViewSet):
authentication_classes = [LoginAuth,]
-方式二:全局配置
REST_FRAMEWORK={
'DEFAULT_AUTHENTICATION_CLASSES':['app01.auth.LoginAuth',]
}
-局部禁用:
authentication_classes = []
认证类
from .models import UserToken
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
class LoginAuth(BaseAuthentication):
def authenticate(self, request):
# 在这里做认证,校验用户是否登录(带了token,并且能查到,就是登录,返回两个值,否则就是没登录,抛异常)
# 用户带的token从哪取?后端人员定的:放在请求地址中
token = request.GET.get('token')
# 通过token查询该token是否是在表中有记录
user_token = UserToken.objects.filter(token=token).first()
if user_token:
return user_token.user, token # 返回两个值,一个是当前登录用户,一个是token
else:
raise AuthenticationFailed('您没有登录')
八、作业
1 登录接口,图书5个接口,出版社5个接口(关联的关系),,使用9个视图子类,5个视图扩展类写
2 图书的必须登录后才能方法,出版社的不登录就能访问
-全局配置,局部禁用
class BookView(ListCreateAPIView):
# authentication_classes = [LoginAuth]
queryset = models.Book.objects.all()
serializer_class = serializer.BookSerializer
class BookDetailView(RetrieveUpdateDestroyAPIView):
# authentication_classes = [LoginAuth]
queryset = models.Book.objects.all()
serializer_class = serializer.BookSerializer
class PublishView(GenericAPIView, ListModelMixin, CreateModelMixin):
queryset = models.Publish.objects.all()
serializer_class = serializer.PublishSerializer
authentication_classes = []
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 PublishDetailView(GenericAPIView, DestroyModelMixin, UpdateModelMixin, RetrieveModelMixin):
queryset = models.Publish.objects.all()
serializer_class = serializer.PublishSerializer
authentication_classes = []
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)
3.级联删除小知识
on_delete=None, # 删除关联表中的数据时,当前表与其关联的field的行为 on_delete=models.CASCADE, # 删除关联数据,与之关联也删除 on_delete=models.DO_NOTHING, # 删除关联数据,什么也不做 on_delete=models.PROTECT, # 删除关联数据,引发错误ProtectedError # models.ForeignKey('关联表', on_delete=models.SET_NULL, blank=True, null=True) on_delete=models.SET_NULL, # 删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空,一对一同理) # models.ForeignKey('关联表', on_delete=models.SET_DEFAULT, default='默认值') on_delete=models.SET_DEFAULT, # 删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值,一对一同理) on_delete=models.SET, # 删除关联数据, a. 与之关联的值设置为指定值,设置:models.SET(值) b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象) 标签:models,self,request,视图,token,class,drf From: https://www.cnblogs.com/zzjjpp/p/16768958.html