drf框架----django-rest-framework

安装

drf 是django的一个扩展应用, windows为例:

python -m pip install django-rest-framework
#或者
python -m pip install djangorestframework

使用时,需在django项目中安装该应用。

#settings.py
INSTALLED_APP = [
...,
'rest_framework', # 若不注册, 会templateNotExists错误
]

 
然后就可以在项目中使用

#视图
from rest_framework.views import APIView
#响应
from rest_framework.response import Response
#序列化,将python对象----转为字典
from rest_framework import serializers
serializers.ModelSerializer
serializers.Serializer

# 视图集
from rest_framework.viewsets import ModelViewSet
  1. APIView 继承了View (django.views.View)
  2. ModelViewSet 继承View

相关工具

postman 模拟发送http请求
collection 存储多个api,可以批量执行测试

APIpost工具 接口测试

解析Json数据
fiddler 抓包工具

主要功能

restful规范

前后端分离的项目中,API设计的规范,在一定程度上达成共识,便于理解。

  1. 使用https进行数据传输,保证数据的安全性

  2. 在域名或者路径中体现API字样,一看即懂
    如https://api.baidu.com/

  3. 在路径中体现版本
    如https://api.baidu.com/v1
    https://api.baidu.com/v2

  4. 数据资源,在路径中均使用名词!!

  5. 请求方式!!,表示操作类型
    GET /api/v1/users/id
    POST 新增
    PUT 更新
    DELETE 删除

  6. 资源过滤,通过查询字符串进行过滤数据
    如https://api.baidu.com/v1/users/?sex=male&offset=10&limit=10&sortby=age&order=desc

  7. 响应状态码
    200, 请求成功
    201, 创建资源成功
    301,永久重定向
    302, 临时重定向
    404, 请求地址不存在
    405, 请求方法不支持
    500, 服务端内部错误

  8. 响应的错误信息error、提示信息msg

  9. 返回资源结果

  10. 响应的数据可以有链接

视图

使用rest_framework必须定义class-based-view

  1. django的View源码
path("/users/login", Login.as_view()) # 类方法, 闭包
@classonlymethod
def as_view(cls, **initkwargs):
   """Main entry point for a request-response process."""
   # 省略不执行部分

   def view(request, *args, **kwargs):
       # 视图类的实例对象
       self = cls(**initkwargs)
       
       if hasattr(self, 'get') and not hasattr(self, 'head'):
           self.head = self.get
       
       # setup给self对象进行属性赋值
       #self.request = request
       #self.args = args
       self.setup(request, *args, **kwargs)
       
       if not hasattr(self, 'request'):
           raise AttributeError(
               "%s instance has no 'request' attribute. Did you override "
               "setup() and forget to call super()?" % cls.__name__
           )

	   # 提交给对应的视图类的方法, 返回响应
       return self.dispatch(request, *args, **kwargs)
   view.view_class = cls
   view.view_initkwargs = initkwargs

   # take name and docstring from class
   update_wrapper(view, cls, updated=())

   # and possible attributes set by decorators
   # like csrf_exempt from dispatch
   update_wrapper(view, cls.dispatch, assigned=())
   return view
  1. APIView视图
    继承django的View,取消CSRF验证。
    @csrf_exampt
    def xxx
     
    或者csrf_exampt(函数)
from rest_framework.views import APIView
from rest_framework.response import Response
class Query(APIView): #APIView 继承View
	# 查看请求能否过来,以及其发过来的数据
    # 前端发送post的数据--> request.data
    # put/delete请求   --> request.data
    # 前端get 查询字符串--> request.query_params
    # 
   
    print(dir(request)

	return Response({})
  1. 视图集
# views.py
from rest_framework.viewsets import ModelViewSet
from users.models import User
from users.serializers import UserSer

# 定义一个视图集
class UserViewSet(ModelViewSet):
	queryset = User.objects.all()
	serializer_class = UserSer

# 主路由urls.py
 from rest_framework.routers import DefaultRouter
 router = DefaultRouter()
 router.register("/users/test", UserViewSet) #对应视图集

urlpatterns += router.urls

重点:

  1. drf 必须定义视图类,且取消了csrf验证
  2. request对象是drf封装的(rest_framework.request.Request),原生的在self._request
  3. request.data POST 请求提交的数据;
    request.query_params GET 请求查询参数
    request.method 请求方法
    request.FILES 文件
  4. 三大认证
    用户认证、权限认证、访问频率

序列化

将模型对象,转为字典,经过Response转为json字符串,便于网络传输。

  1. 小组长,完成如下工作
  • 创建一个gitee仓库;
  • 创建一个Django项目,配置跨域、drf、mysql、静态文件;
  • 创建一个users 应用,并注册;
  • 将Django项目推送到gitee的仓库。
    登录自己的gitee账号,创建一个组织,在组织内部创建仓库:
    在这里插入图片描述
    添加 开发成员
    在这里插入图片描述
    负责人克隆项目到本地,并创建基本的项目master,然后提交,供组员在master基础上创建分支,并行开发。
# 克隆远程仓库
git clone https://gitee.com/laufing_org/django_2022_3_19.git

cd 仓库
# 配置签名,否则在提交时,会限制,每一个开发者克隆项目后都必须配置
git config user.name laufing
git config user.email 944582529@qq.com

# 创建项目
django-admin startproject django_laufing
# 项目其他的配置

# 提交本地仓库,并推送到远程仓库
git status
git add ./
git commit -m '项目主分支master, 起始'

git remote -v

# 没有远程地址时,需要添加
git remote add origin  url

# 推送
git push origin master

注意:如果远程仓库初始化了一个git仓库,则本地git init产生的仓库,在推送时,会有冲突,需要先合并。

  1. 组员A,克隆项目主分支master,并完成如下工作
  • 配置仓库签名,创建分支branch_username,并切换分支
  • 在users应用中,定义一个User模型类,继承django内建的AbstractUser
  • 创建一个serializers.py, 并定义User模型类的局部序列化器(序列化username、mobile)
  • 提交本地仓库
# 模型类 局部属性序列化
# models.py
class User(AbstractUser):
	mobile = models.CharField("手机号", max_length=11)

	def __str__(self):
		return self.username
	
	class Meta:
		db_table = "user_tb"
		verbose_name_plural = "用户表"
# 注意配置 AUTH_USER_MODEL = 'users.User'

# 序列化器
# serializers.py
from rest_framework import serializers
class UserSerializer(serializers.Serializer): # 局部序列化
	username = serializers.CharField()   # 只序列化部分属性
	mobile11 = serializers.CharField(source='mobile') #source标识模型类中的字段/外键.属性/ 模型类中的方法,如'test'
	# 需要重写create & update方法
	
	# 序列化方法字段, 搭配一个函数get_字段名
	authors = serializers.SerializerMethodField()
	def get_authors(self, instance):
		data = instance.authors.all()
		return data   # 返回什么 authors就是什么数据

	
# 模型类的序列化器,所有类属性或者指定
class UserSer(serializers.ModelSerializer):
	password = serializers.CharField(max_length=64, write_only=True)
	class Meta:
		model = User
		fields = "__all__"
		#fields = (xx,xx,..., "password")
		#exclude = (xx,...) 排除 不与fields一起使用
		#read_only_fields = (xx,xx,)有效
		#write_only_fields = (,) 无效
		extra_kwargs = {
			'mobile':{'read_only': True}
		}
# 反序列化不需要重写create& update

其他字段类型
serializers.BooleanField()
serializers.IntegerField()
serializers.DecimalField()
serializers.DateField()
serializers.DateTimeField()
serializers.ChoiceField()
serializers.ImageField()
serializers.FileField()
serializers.EmailField()
serializers.SlugField() 正则
serializers.URLField()

  1. 成员B,克隆项目主分支master,并完成如下工作
  • 配置签名,创建分支(vscode会自动切换到该分支),并发布
  • 在users应用中,配置路由/users/user/用户id
  • 定义视图类UserAPIView,接收GET 请求,实现查询用户,序列化并返回响应
  • 用户模型类User, 序列化器UserSerializer
  • 发布分支,即推送到远程仓库git push origin branch_worker_b
path('user/<int:uid>', UserAPIView.as_view()),

# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
class UserAPIView(APIView):
	def get(self, request, uid):
		# 查询一个对象
		user = User.objects.filter(id=uid).first()
		# 序列化一个对象
		user_ser = UserSerializer(user)
		#查询多个
		#user_queryset = User.objects.all()
		#user_queryset_ser = UserSerializer(user_queryset, many=True)
		
		# 返回响应
		return Response(user_ser.data)
		# return Response({"code": 200, "msg": '成功', 'user': user_ser.data})
  1. 小组长汇总
  • 拉取项目, 合并分支。在本地原来的仓库中拉取组员更新的内容git pull origin branch_worker,自动合并到master主分支。(git pull origin,默认拉取master分支)
  • 本地创建mysql数据库,并完成模型类迁移
  • 插入测试数据
  • API接口测试, 浏览器输入地址http://host/users/user/1
    成功!!
    在这里插入图片描述

反序列化

将json字符串经过request转为字典,然后使用序列化器转为模型对象,还可以进行数据校验,如数据类型是否满足要求。

class UserSerializer(serializers.Serializer):
	# 需要反序列化的属性
	username = serializers.CharField(max_length=10, min_length=5) # 参数用于校验
	mobile = serializers.CharField(max_length=11, min_length=11)

	#更新时 必须重写update方法   implement
	def update(self, instance, data):
		instance.username = data.get("username")
		instance.mobile = data.get("mobile")
		instance.save()
	# 新增时,必须重写create方法
	def create(self, data):
		user = User.objects.create(**data)
		return user

# 视图类中 put更新
def put(self, request, uid):
	user = User.objects.filter(id=uid).first()
	user_ser = UserSerializer(instance=user, data=request.data)  # 使用data进行更新
	# 校验
	if user_ser.is_valid():
		user_ser.save()
		return Response(user_ser.data)
	else:
		return Response(user_ser.errors)
# 新增
def post(self, request):
	user_ser = UserSerializer(data=request.data)
	if user_ser.is_valid():
		user_ser.save()
		return Response(user_ser.data)
	else:
		return Response(user_ser.errors)
  1. 自带校验参数:
    max_length
    min_length
    max_value
    min_value
    allow_blank = True/False 是否允许空
    trim_whitespace 是否截断空白符

  2. 校验规则不够,自己写规则函数,在UserSerializer类中

from rest_framework.exceptions import ValidationError
class UserSerializer(serializers.Serializer):
	...
	# 局部验证
	def validate_mobile(self, mobile): # validate_字段
		if not mobile.startswith("185"):
			raise ValidationError("xxx")
		else:
			return mobile
	# 全局验证
	def validate(self, data):
		username = data.get("username")
		mobile = data.get("mobile")
		if mobile.startswith("1"):
			return data
		else:
			return ValidationError("xxx")
  1. serializers.CharField(validators=[自定义函数地址])
  2. 限制的参数
    readonly,字段仅仅用于序列化输出
    writeonly,字段仅仅用于反序列化输入
    required=True,反序列化时必须传入
    default, 反序列化时的默认值
    allow_null, 反序列化时是否允许None
    validators,该字段使用的验证器

响应

对响应处理,返回带有web界面的响应

from rest_framework.response import Response

return Response(data, content_type, status, headers,...)
data, 返回的数据字典
content_type, 内容类型
status, 状态码, 可以使用rest_framework.status中的常量
headers, 响应头字典 , 如{"token":"xx"}

GenericAPIView

GenericAPIView 和 5个扩展类

from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin # 查看所有
from rest_framework.mixins import CreateModelMixin # 新增一个
from rest_framework.mixins import UpdateModelMixin # 更新一个
from rest_framework.mixins import DestroyModelMixin #删除一个
from rest_framework.mixins import RetrieveModelMixin

# 两个路由
# 查看所有、新增一个
path("GenericAPIView/", UserView1.as_view()),
# 查看一个、更新一个、删除一个, 必须是pk
path("GenericAPIView/<int:pk>/", UserView2.as_view()),

# 基于GenericAPIView 和5个扩展类的视图
class UserView1(GenericAPIView, ListModelMixin, CreateModelMixin):
    queryset = User.objects.all()
    serializer_class = UserSer

    def get(self, request):
        return self.list(request)

    def post(self, request):
        return self.create(request)

class UserView2(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    queryset = User.objects.all()
    serializer_class = UserSer

    def get(self, request, pk):
        return self.retrieve(request, pk)

    def put(self, request, pk):
        return self.update(request, pk)

    def delete(self, request, pk):
        return self.destroy(request, pk)

# 浏览器中测试即可

进一步改进 GenericAPIView 9个视图子类

from rest_framework.generics import ListAPIView, CreateAPIView, UpdateAPIView, DestroyAPIView,RetrieveAPIView
#前端只需发送相应的请求即可
#path("/books/", BookAPIView1.as_view())
class BookAPIView1(ListAPIView, CreateAPIView):
	#查看所有, 新增一个  ListCreateAPIView
    queryset = Book.objects.all()
    serializer_class = BookSer  #新增时,注意与序列化器匹配

#path('/books/book/<int:pk>', BookAPIView2.as_view())
class BookAPIView2(UpdateAPIView, DestroyAPIView, RetrieveAPIView):
	#更新一个、删除一个、查看一个 RetrieveUpdateDestroyAPIView
	queryset = Book.objects.all()
	serializer_class = BookSer

视图集

path("v1/users/", UserViewSet.as_view({"get":"list", 'post':'create'})),

# 必须是pk
path("v1/users/user/<int:pk>/",  UserViewSet.as_view({"get":"retrieve", "put":"update", 'delete':"destroy"})),

# 视图集合
from rest_framework.viewsets import ModelViewSet
class UserViewSet(ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSer

自定义action
将请求定向到自定义的函数,则需继承ViewSetMixin, APIView

# 路由,实现get请求 对应到自己定义的action
path("ViewSetMixin/", UserViewSet.as_view({"get":"my_func", 'post': "my_func2"}))

# 视图类
from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet
from rest_framework.viewsets import ViewSetMixin
class UserViewSet(ViewSetMixin, APIView):
	# 自定义的函数
    def my_func(self, request):

       print("自定义的action")

       return Response({"code": 200, "msg": "自定义的action"})

	def my_func2(self, request):
		return Response(xxx)
	

传入一个pk, 也是可以的;

路由

自动生成路由

# 主路由中urls.py
from rest_framework.routers import DefaultRouter
from users.views import UserViewSet

router = DefaultRouter()
router.register("user", UserViewSet)

urlpatterns = [
    path('admin/', admin.site.urls),

    # 图形验证码
    path("v1/imageCode/<str:uuid_>/", gen_image_code),

    # 用户注册
    path('v1/users/', include("users.urls")),
]

urlpatterns += router.urls

# 对应到视图集
from rest_framework.viewsets import ModelViewSet
from users.models import User
from users.serializers import UserSer
class UserViewSet(ModelViewSet):
	queryset = User.objects.all()
	serializer_class = UserSer

会自动生成路由
/user/ 查看所有
/user/ 新增一个
/user/id 查看一个、更新一个、 删除一个

认证

通过请求头中的Authorization检查用户是否登录
定义认证类,并继承BaseAuthentication,重写authenticate方法;
认证通过,返回user对象,auth口令
认证失败,raise AuthenticationFailed

print(“认证结果:”, request.user, request.auth, request.authenticators)

  1. 创建一个users应用,并定义模型类(继承AbstractUser)
  2. 迁移,并创建一个用户
  3. 定义LoginAPIView,让用户登录,并生成token
  4. 定义CheckUserAPIView,get请求携带token,认证

使用APIpost认证时,自己添加Authorization头部时,会报错,如下:
在这里插入图片描述
此时应用使用下面这种方式:
在这里插入图片描述
服务端要对token (Bearer abcxxx)分割一下

核心代码:

from django.contrib.auth.hashers import check_password
import jwt
from django.conf import settings
from datetime import timedelta
from datetime import datetime
# 用户登录
class LoginAPIView(APIView):

    def post(self, request):
        print("请求体数据:", request.data, type(request.data))
        username = request.data.get("username")
        password = request.data.get("password")

        # 查询用户对象
        user = User.objects.filter(username=username).first()
        # print("user:", user)
        if user:
            validate = check_password(password, user.password)

            if validate:
                payload = {
                    "uid": user.id,
                    'uname': user.username
                }
                expire = 300
                token = self.generate_token(payload, expire)

                res = Response({"code": 200, "msg": "登录成功"})

                # 设置响应头
                res['token'] = token
                res['author'] = 'laufing'

                return res

        return Response({"code": 204, "msg": '用户名或密码错误'})

    @staticmethod
    def generate_token(payload, expire, secret=None):
        _payload = {
            "exp": time.time() + expire  # 当前时间戳加 s
        }
        # 将用户信息字典更新到自己内部
        _payload.update(payload)

        if secret:
            ...
        else:
            secret = settings.SECRET_KEY

        return jwt.encode(payload=_payload, key=secret, algorithm="HS256")

对get等其他请求,开启认证

# 用户的认证
from rest_framework.authentication import BaseAuthentication
from jwt.exceptions import ExpiredSignature, DecodeError
from rest_framework.exceptions import AuthenticationFailed

# 自定义认证类
class MyAuthentication(BaseAuthentication):
	# 必须重写authenticate方法
    def authenticate(self, request):
    	# 获取请求头中的Authorization  即jwt token
        token = request.headers.get("Authorization")
        print("token:", token)
        # apipost 开启认证时,token以Bearer开头
        if token.startswith("Bearer"):
            token = token.split(" ")[-1]
        try:
            payload = jwt.decode(token, key=settings.SECRET_KEY, algorithms=['HS256'])
        except ExpiredSignature:
        	# 认证失败  抛出异常
            raise AuthenticationFailed("token已过期")

        except DecodeError:
            raise AuthenticationFailed("非法的token")

        user = User.objects.filter(id=payload.get("uid")).first()
        # 认证通过,返回user对象, auth口令
        return user, token

# 处理需认证的请求
class CheckUserAPIView(APIView):
	# 认证的类  ---局部认证
    authentication_classes = [MyAuthentication,]
	
	# 在进入get视图前,先认证
    def get(self, request):
		# request.user
		# request.auth
		# request.authenticators
        if request.user:

            return Response({"code": 200, "msg": "验证通过"})

        else:
            return Response({"code": 204, "msg": "验证不通过"})

自定义action的方式,必须继承ViewSetMixin, APIView

class CheckUserAPIView(ViewSetMixin, APIView):
    authentication_classes = [MyAuthentication]
    queryset = User.objects.all()
    serializer_class = UserSer

    @action(methods=["GET"], detail=False)  # detail 是否允许传入pk
    def myget(self, request):
        print(request.user)
        print(request.auth)
        print(request.authenticators)

        if request.user:

            return Response({"code": 200, "msg": "验证通过"})

        else:
            return Response({"code": 204, "msg": "验证不通过"})

全局认证
在项目的settings.py中配置,并且

  1. 关闭局部认证
  2. 自定义的认证类不能放入views.py 中
    每个请求过来都认证用户是否登录
# 配置全局认证

REST_FRAMEWORK = {
	"DEFAULT_AUTHENTICATION_CLASSES": ['users.auth.MyAuthentication'],
}

权限

参考链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

laufing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值