DRF总结

纵饮孤独 提交于 2019-12-10 11:48:28

转自https://blog.csdn.net/weixin_44143222/article/details/88878072

web开发的两种模式:前后端不分离和前后端分离

RestFul API接口设计风格:前后端分离被广泛采用

使用Django基础自定义Rest API接口

DRF框架: 提高开发Rest API接口的效率

web开发的两种模式
前后端不分离:前端看到的效果是由后端进行控制的

缺点:只适用于纯网页的应用。

优点:有利于网站的SEO优化。
————————————————
版权声明:本文为CSDN博主「zyj1189」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_44143222/article/details/88878072


前后端分离:后端只返回前端所需的数据,至于数据怎么进行展示,由前端自己控制。

优点:可以对接不同类型的客户端。

缺点:不利于SEO优化


RestFul API接口设计风格介绍
统一接口设计风格:
    1.URL地址尽量使用名词,不要出现动词
    2.使用不同的请求方式,代表要执行不同的操作
    (GET)获取 POST(新增) PUT(修改)DELETE(删除)
    不常用:PATCH(修改) HEAD(只返回请求头没有请求体) OPTIONS(获取信息)
    3.访问URL地址时,如果有一些过滤的参数,参数可以放到查询字符串中
    4.响应数据:
        GET /books/:返回所有的图书数据
        GET /books/1/:返回id为1的图书数据
        POST /books/:将新增的图书数据返回
        PUT /books/1/:将修改的图书数据返回
        DELETE /books/1:返回空文档
        获取|修改:200
        新增:201
        删除:204
        参数有误:400
        服务器出错:500
    5.响应数据的格式:json
    
了解:1.域名:使用专有域名
     2.版本:将版本信息放在url地址
     3.错误:将错误信息返回
     4.在访问api接口时,将和接口相关的其他API接口的地址也在响应数据中返回
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Django自定义RestAPI
需求:
    设计一套符合RestAPI风格的接口,提供以下5个接口:
        1. 获取所有图书数据:GET /books/
        2. 新增一本图书数据:POST /books/
        3. 获取指定的图书数据(根据id):GET /books/(?P<pk>\d+)/
        4. 修改指定的图书数据(根据id):PUT /books/(?P<pk>\d+)/
        5. 删除指定的图书数据(根据id):DELETE /books/(?P<pk>\d+)/
1
2
3
4
5
6
7
# 2个类视图
class BookListView(View):
    # GET /books/
    def get(self, request):
        """
        获取所有图书数据:
        1. 查询所有图书的数据
        2. 将图书的数据进行返回
            数据格式:json 状态码:200
        """
        # 1. 查询所有图书的数据
        books = BookInfo.objects.all() # QuerySet

        # 组织数据
        books_li = []
        for book in books:
            book_dict = {
                'id': book.id,
                'btitle': book.btitle,
                'bpub_date': book.bpub_date,
                'bread': book.bread,
                'bcomment': book.bcomment,
                'image': book.image.url if book.image else ''
            }

            books_li.append(book_dict)

        # 2. 将图书的数据进行返回
        # 注意点:将list转换为json数据时,需要将safe设置False
        return JsonResponse(books_li, safe=False)

    # POST /books/
    # 参数:客户端传递 btitle,bpub_date,通过json传递
    def post(self, request):
        """
        新增一本图书数据:
        1. 获取参数并进行校验:request.body->decode->json.loads
        2. 创建图书并添加到数据库
        3. 将新增的图书数据进行返回
            数据格式:json 状态码:200
        """
        pass


class BookDetailView(View):
    # GET /books/(?P<pk>\d+)/
    def get(self, request, pk):
        """获取指定的图书数据(根据id)"""
        pass

    # PUT /books/(?P<pk>\d+)/
    def put(self, request, pk):
        """修改指定的图书数据(根据id)"""
        pass

    # DELETE /books/(?P<pk>\d+)/
    def delete(self, request, pk):
        """删除指定的图书数据(根据id)"""
        pass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
DRF框架-RestAPI接口的核心工作
序列化:将模型对象转换为字典或者json的过程,叫做序列化的过程。
反序列化:将客户端传递的数据保存转化到模型对象的过程,叫做反序列化的过程。

核心:
1. 将数据库数据序列化为前端所需要的格式,并返回;
2. 将前端发送的数据反序列化为模型类对象,并保存到数据库中。
1
2
3
4
5
6
Django RestFrameWork 简介/安装
作用:提高RestAPI接口开发的效率
关键功能:
    序列化器:序列化和反序列化
    类视图,MiXin扩展类:简化视图代码的编写
安装: pip install djangorestframework
1
2
3
4
5
在settings.py的INSTALLED_APPS中添加’rest_framework’。

INSTALLED_APPS = [
    ...
    'rest_framework', # 添加
]
1
2
3
4
DRF框架功能演示
序列化器Serializer-功能&知识点说明
功能:进行数据的序列化和反序列化
使用:首先定义序列化器类

序列化功能:将对象转换字典。
    1.序列化单个对象
    2.序列化多个对象
    3.关联对象的嵌套序列化

反序列化功能:
    数据校验:
        1.基本验证
        2.补充验证
    数据保存
        1.数据新增create
        2.数据更新update
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
序列化器Serializer-定义&基本使用
定义模型类:

    class 模型类(modles.Model):

        模型字段 = models.字段类型(选项参数)

定义序列化器类:

    from rest_framework import serializers
    class 序列化器类(serializers.Serializer):
        序列化器字段= serializers.字段类型(选项参数)
        
序列化器类(instance=None,data={},**kwargs)
    1.进行序列化操作,将对象传递给instance
    2.进行反序列化操作,将数据传递给data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
在tests.py文件中进行演示:

import os

import django
from django.test import TestCase

# Create your tests here.
if not os.environ.get("DJANGO_SETTINGS_MODULE"):
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "drf_demo.settings")

# 让django进行一次初始化
import django
django.setup()

from rest_framework import serializers


class User(object):
    """用户类"""

    def __init__(self, name, age):
        self.name = name
        self.age = age


class UserSerializer(serializers.Serializer):
    name = serializers.CharField()
    age = serializers.IntegerField()

# 序列化基本使用
# if __name__ == '__main__':
#     # 用户对象
#     user = User(name="Monkey", age=18)
#     res = UserSerializer(user)
#     dict = res.data
#     print(dict)

# 反序列化-基本使用-数据校验
if __name__ == '__main__':
    # 假如现在客户端给服务器传递了两个参数:那name,age,利用这两个数据创建一个用户的对象,但是需要先进行数据校验
    req_data = {
        'name':"Moneky",
        # "age":24
    }
    # 反序列化-数据校验
    serializer = UserSerializer(data=req_data)
    res = serializer.is_valid()
    print(res)

    # 获取校验出错的信息
    print(serializer.errors)

    # 获取校验之后的数据
    print(serializer.validated_data)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
字段类型&选项参数说明
在定义序列化器类的字段时,write_only和read_only默认值为False,说明这个字段既在序列化时使用,也在反序列化时使用。
通用选项参数:
    write_only: 设置为True,该字段只在序列化时使用,反序列化操作时不使用
    read_only: 设置为True,该字段只在序列化时使用,反序列化时不使用
    default:设置序列化和反序列化时所使用的默认值
    requird:默认值是True,指明在进行反序化时此字段是否必须传入
    allow_null    表明该字段是否允许传入None,默认False
    validators    该字段使用的验证器
    error_messages    包含错误编号与错误信息的字典
    label    用于HTML展示API页面时,显示的字段名称
    help_text    用于HTML展示API页面时,显示的字段帮助提示信息

1
2
3
4
5
6
7
8
9
10
11
12
序列化操作-序列化单个对象和多个对象
序列化单个对象:
    book = BookInfoSerializer(id=1)
    serializer = BookInfoSerializer(book)
    serializer.data
序列化多个对象:
    books = BookInfo.objects.all() # QuerySet
    serializer = BookInfoSerializer(books, many=True)
    serializer.data
1
2
3
4
5
6
7
8
序列化单个对象:


序列化多个对象:


序列化操作-关联对象嵌套序列化
hero = HeroInfo.objects.get(id=1)
# 获取和英雄关联图书对象
hero.hbook

# 1. 将关联对象序列化为关联对象主键 PrimaryKeyRelatedField
{
 "id": 1,
 "hname": "孙悟空",
 "hgender": 0,
 "hcomment": "七十二变",
 "hbook": "<hbook图书主键>"
}

# 2. 使用指定的序列化器将关联对象进行序列化
{
 "id": 1,
 "hname": "孙悟空",
 "hgender": 0,
 "hcomment": "七十二变",
 "hbook": {
     "id": "图书id",
     "btitle": "图书标题",
     "bpub_date": "出版日期",
     "bread": "阅读量",
     "bcomment": "评论量",
     "image": "封面图片"
 }
}

# 3. 将关联对象序列化为关联对象模型类__str__返回值 StringRelatedField
{
 "id": 1,
 "hname": "孙悟空",
 "hgender": 0,
 "hcomment": "七十二变",
 "hbook": "<图书名称>"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 注意:如果和对象关联的对象有多个,在定义嵌套序列化的字段时,需要指明many=True
book = BookInfo.objects.get(id=1)
# 获取和book对象关联英雄数据
book.heroinfo_set.all()

{
     "id": 1,
     "btitle": "西游记-2",
     "bpub_date": "1998-10-10",
     "bread": 10,
     "bcomment": 0,
     "image": null,
    "heroinfo_set": [
        '<英雄id>',
        ...
    ]
}

{
     "id": 1,
     "btitle": "西游记-2",
     "bpub_date": "1998-10-10",
     "bread": 10,
     "bcomment": 0,
     "image": null,
    "heroinfo_set": [
        {
            'id': '<英雄id>',
            'hname': '<英雄名称>',
            'hgender': '<英雄性别>'
            'hcomment': '<英雄备注>'
        },
        ...
    ]
}

{
     "id": 1,
     "btitle": "西游记-2",
     "bpub_date": "1998-10-10",
     "bread": 10,
     "bcomment": 0,
     "image": null,
    "heroinfo_set": [
        '<英雄名称>',
        ...
    ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48


反序列化操作
数据校验&补充验证
req_data = {
    'btitle': '测试图书',
    'bpub_date': '2010-1-1'
}

serializer = BookInfoSerializer(data=req_data)
# 校验
res = serializer.is_valid()
# 获取错误信息
serializer.errors
# 获取校验之后的数据
serializer.validated_data

补充验证3种方法:
1. 指定特定字段选项参数`validators`来指明补充验证函数
2. 在序列化器类中定义特定的方法`validate_<fieldname>`来针对特定字段进行补充验证
3. 定义validate方法进行补充验证:可以结合多个字段内容进行补充验证
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17


反序列化操作-数据保存(新增&更新)
# 注意:进行数据保存之前必须通过反序化数据验证

# 数据保存
serializer.save()

调用序列化器save方法时,save方法内部可能会调用序列化器类的create方法和update方法,我们可以在create方法中实现数据新增,在update方法中实现数据更新。

serializer = BookInfoSerializer(data=req_data)
serializer.is_valid()
serializer.save() # 调用序列化器类中的create

serializer = BookInfoSerializer(book, data=req_data)
serializer.is_valid()
serializer.save() # 调用序列化器类中的update
1
2
3
4
5
6
7
8
9
10
11
12
13
14


Serializer-ModelSerializer序列化器类的使用
from rest_framework import serializers

serializers.ModelSerializer:

    是Serizalizer类的子类,在定义序列化器类时,如果序列化器类针对的是某个模型类,可以直接继承于ModelSerializer。
    好处:
    1.序列化器类字段可以依据模型类字段自动生成。
    2.ModelSerializer里面已经实现create和update方法。
1
2
3
4
5
6
7
8


DRF类视图-APIView
APIView视图基类:
    Django框架View类的子类,在View类的基础上封装了一些功能。

功能如下:
    1. 视图request参数变成了Request类的对象,不再是Django原始HttpRequest类的对象;
        request.data: 保存解析之后的请求体中数据,已经解析为了字典或类字典(QueryDict)。request.POST|request.body|request.FILES
        request.query_params: 保存解析之后的查询字符串。request.GET
        
    2. 响应时可以统一返回Response类的对象,DRF框架会根据客户端请求头的`Accept`将响应数据转换为对应数据格式进行返回,默认返回json,仅支持html或json;
        `Accept: text/html`:将响应数据转换为html进行返回
        `Accept: applicaiton/json`:将响应数据转换为json进行返回
        return Response(响应数据)
    
    3. 异常处理:如果视图出现了未处理的异常,DRF框架会对异常进行处理,并将处理的错误响应返回给客户端;
    4. 认证&权限&限流
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
案例-继承APIView改写RestAPI接口

类视图-GenericAPIView视图基类的功能&使用

GenericAPIView视图基类:
    继承自APIView,在APIView类的基础上封装了一些功能。
    
功能如下:
    1. 封装了操作序列化器的属性和方法:
        属性:
            serializer_class:指明视图所使用的序列化器类;
        方法:
            get_serializer_class:获取视图所使用的序列化器类;
            get_serializer:创建一个视图所使用的序列化器类的对象;
            
        class 类视图(GenericAPIView):
            serializer_class = <序列化器类>
    
    2. 封装了查询数据库的属性和方法:
        属性:
            query_set:指明视图所使用的查询集
        方法:
            get_queryset:获取视图所使用的查询集
            get_object:从视图所使用的查询集中查询指定的对象(默认根据pk主键进行查询)
            
        class 类视图(GenericAPIView):
            queryset = <查询集>
            
    3. 过滤&分页
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
类视图-GenericAPIView视图基类的源码说明

补充说明:
    定义类视图之后,类视图对象有一个属性kwargs:是一个字典,保存从url地址中提取的所有命名参数;

    # /books/(?P<pk>\d+)/
    class 类视图(View|APIView|GenericAPIView):
        def 方法(self):
            # self.kwargs['pk']
            
        def get(self, request, pk):
            """
            self.kwargs: 是一个字典,保存从url地址中提取的所有命名参数;
            """
            pass
1
2
3
4
5
6
7
8
9
10
11
12
13
类视图-Mixin扩展类的功能&使用

继承自GenericAPIView之后,使用GenericAPIView中提供的操作序列化器的函数和数据库查询的函数写出的代码变成了通用代码,这些通用代码抽取之后,就是DRF框架提供的5个Mixin扩展类。

Mixin扩展类:
    ListModelMixin:list,封装了获取一组数据通用流程。
    CreateModelMixin:create,封装了新增一条数据通用流程。
    RetrieveModelMixin:retrieve,封装了获取指定数据通用流程。
    UpdateModelMixin:update,封装了更新指定数据通用流程。
    DestroyModelMixin:destroy,封装了删除指定数据通用流程。
    
GenericAPIView通常配合Mixin扩展类进行使用。
1
2
3
4
5
6
7
8
9
10
类视图-子类视图类的功能&使用说明

9个子类视图类:
    同时继承了GenericAPIView和对应Mixin扩展类,同时在子类视图中提供了对应的请求处理函数。
    
    from rest_framework import generics
    
    class ListAPIView(ListModelMixin, GenericAPIView):
        def get(self, request, *args, **kwargs):
            return self.list(request, *args, **kwargs)
            
    class CreateAPIView(CreateModelMixin, GenericAPIView):
        def post(self, request, *args, **kwargs):
            return self.create(request, *args, **kwargs)
1
2
3
4
5
6
7
8
9
10
11
12
视图集-ViewSet视图集的功能&使用

视图集:将操作同一组资源的处理函数放在同一个类中,这个类就是视图集。

视图集和类视图的区别?
答:实现同一组接口时,如果使用类视图可能需要多个类视图,而使用视图集时只需要一个视图集。
    比如:实现图书管理的5个接口时,使用类视图用了2个类视图:BookListVIew和BookDetailView,如果使用视图集只需要一个即可。
    
基本使用:
    1. 继承自ViewSet(继承自ViewSetMixin和APIView)
    2. 视图集中的处理函数不再以请求方式(比如:get,post等)命名,而是以对应的action操作命名,常见操作如下:
        list:获取一组数据
        create:创建一条数据
        retrieve:获取指定数据
        update:修改指定数据
        destroy:删除指定数据
    3. 在urls.py进行url地址配置时需要明确指明某种请求方式请求某个地址对应的是视图集中的哪个处理函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
视图集-视图集父类GenericViewSet使用

继承自ViewSetMixin和GenericAPIView,可以配合Mixin扩展类提供对应的处理函数
1
视图集-视图集父类ModelViewSet和ReadOnlyModelViewSet使用

ModelViewSet:继承了5个Mixin扩展类,同时继承了GenericViewSet
ReadOnlyModelViewSet:继承了ListModelMixin, RetireveModelMixin,同时继承了GenericViewSet

需求:
    写一个视图集,提供一下2个API接口:
    1. 获取所有的图书 GET /books/ -> list
    2. 获取指定的图书 GET /books/(?P<pk>\d+)/ -> retrieve
    
    class BookInfoViewSet(ReadOnlyModelViewSet):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializer
1
2
3
4
5
6
7
8
9
10
11
1)ModelSerializer

​    Serializer和ModelSerializer区别?

​    答:ModelSerializer是Serializer类子类,如果序列化器类针对是某个模型类时,可以直接继承于ModelSerializer。

​    好处:

​    1. 字段自动生成

​ 2. create和update已经实现

2)APIView视图基类

​    APIView和View区别?

​    答:APIView是View子类。

​    功能:

request请求对象->Request

request.data:保存解析之后的请求体数据。

request.query_params:保存解析之后的查询字符串数据。

2. 响应对象:返回Response
1
return Response(响应数据)

3. 异常处理

4. 认证&权限&限流
1
2
3
3)GenericAPIView视图基类

​    GenericAPIView和APIView区别?

​    答:GenericAPIView是APIView类的子类,在APIView基础上封装操作序列化器和数据库查询的相关属性和方法。

​    序列化操作:

​    属性:serializer_class

​    方法:

​    get_serializer_class

​    get_serializer

​    数据库操作:

​    属性:queryset

​    方法:

​    get_queryset

​    get_object

4)5个Mixin扩展类

​    Mixin扩展类需要配合GenericAPIView进行使用

​    ListModelMixin

​    CreateModelMixin

​    RetrieveModelMixin

​    UpdateModelMixin

​    DestroyModelMixin

​    5)9个子类视图类

​    继承GenericAPIVIew和对应的Mixin扩展,而且提供了对应的请求处理方法。

​    6)视图集

​    类视图和视图集区别?

​    答:实现同一组接口时,如果使用类视图可能需要多个类视图,而使用视图集时只需要一个视图集。

# 需求1:写一个类视图,提供以下1个接口
    1. 获取所有的图书数据 get /books/
    # 1. APIView
    class BookListView(APIView):
        def get(self, request):
            books = BookInfo.objects.all()
            serializer = BookInfoSerializer(books, many=True)
            return Response(serializer.data)
        
   # 2. GenericAPIView
    class BookListView(GenericAPIView):
        # 指定视图所使用的序列化器类
        serializer_class = BookInfoSerializer
        # 指定视图所使用的查询集
        queryset = BookInfo.objects.all()
        
        def get(self, request):
            qs = self.get_queryset()
            serializer = self.get_serializer(qs, many=True)
            return Response(serializer.data)
   
   # 3. Mixin扩展类
   class ListModelMixin(object):
        def list(self, request, *args, **kwargs):
            qs = self.get_queryset()
            serializer = self.get_serializer(qs, many=True)
            return Response(serializer.data)
        
   class BookListView(ListModelMixin, GenericAPIView):
        # 指定视图所使用的序列化器类
        serializer_class = BookInfoSerializer
        # 指定视图所使用的查询集
        queryset = BookInfo.objects.all()
        
        def get(self, request):
            return self.list(request)
   
    # 4. 子类视图:ListAPIView
    class ListAPIView(ListModelMixin, GenericAPIView):
        def get(self, request, *args, **kwargs):
            return self.list(request, *args, **kwrags)
        
    class BookListView(ListAPIView):
        # 指定视图所使用的序列化器类
        serializer_class = BookInfoSerializer
        # 指定视图所使用的查询集
        queryset = BookInfo.objects.all()
    
# 需求2:写一个类视图,提供以下2个接口
    1. 获取指定的图书数据 get /books/(?P<pk>\d+)/
    2. 修改指定的图书数据 put /books/(?P<pk>\d+)/
    
    # 1.  APIView
    class BookDetailView(APIView):
        def get(self, request, pk):
            try:
                book = BookInfo.objects.get(pk=pk)
            except BookInfo.DoesNotExist:
                raise Http404
            
            serializer = BookInfoSerializer(book)
            return Response(serializer.data)
        
          def put(self, request, pk):
            try:
                book = BookInfo.objects.get(pk=pk)
            except BookInfo.DoesNotExist:
                raise Http404
            
            # 反序列化-数据校验
            serializer = BookInfoSerializer(book, data=request.data)
            serializer.is_valid(raise_exception=True)
            
            # 反序列化-数据保存(update)
            serializer.save()
            
            return Response(serializer.data)
        
    # 2. GenericAPIView
    class BookDetailView(GenericAPIView):
        serializer_class = BookInfoSerializer
        queryset = BookInfo.objects.all()
        
        def get(self, request, pk):
            book = self.get_object()
            
            serializer = self.get_serializer(book)
            return Response(serializer.data)
        
          def put(self, request, pk):
            book = self.get_object()
            
            # 反序列化-数据校验
            serializer = self.get_serializer(book, data=request.data)
            serializer.is_valid(raise_exception=True)
            
            # 反序列化-数据保存(update)
            serializer.save()
            
            return Response(serializer.data)
        
    # 3. Mixin扩展类
    class BookDetailView(RetrieveModelMixin, UpdateModelMixin, GenericAPIView):
        serializer_class = BookInfoSerializer
        queryset = BookInfo.objects.all()
        
        def get(self, request, pk):
            return self.retrieve(request, pk)
        
          def put(self, request, pk):
            return self.update(request, pk)
        
    # 4. 子类视图:RetrieveUpdateAPIView
    class BookDetailView(RetrieveUpdateAPIView):
        serializer_class = BookInfoSerializer
        queryset = BookInfo.objects.all()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
APIView->GenericAPIView->Mixin扩展类->子类视图

视图集中添加额外的action处理方法
注意:命名不要冲突

需求:在BookInfoViewSet视图集中再添加2个API
    1.获取id最新的图书信息
    2.修改指定图书的阅读量
1
2
3
视图集对象action属性的作用与应用说明
视图集对象.action:获取所有执行的操作
应用场景:
    视图集中多个API接口中使用的序列化器类和查询集不一样。
    可以重写get_serializer_class和get_queryset,根据不同的操作返回不同的序列化器和查询集。
1
2
3
4
路由Router
路由Router-路由Router的作用和使用
作用:动态生成视图集中处理函数的url配置项。

使用:
1. 创建Router类的对象
    from rest_framework.routers import SimpleRouter, DefaultRouter
    router = SimpleRouter()
2. 注册视图集
    router.register(<prefix>, <viewset>, <base_name>)
    例如:router.register('books', views.BookInfoViewSet, base_name='books')
3. 将动态生成的配置项列表添加到urlpatterns中
    urlpatterns += router.urls
1
2
3
4
5
6
7
8
9
10
11
视图集中额外添加处理函数配置项生成
需要给额外添加处理函数添加action装饰器:
    from rest_framework.decorators import action
    
    # detail指明生成配置项时,是否需要从地址提取参数,需要True,不需要就是False
    @action(methods=['<请求方式>'], detail=False|True):
    def <额外处理函数>(...):
        ...
1
2
3
4
5
6
7
SimpleRouter路由生成的规则说明


DefaultRouter的使用和说明


DRF框架其他功能
-认证&权限
认证:判断访问的用户是谁?
    默认全局认证方式:session认证和基本认证
    修改全局认证方式
    修改指定视图认证方式
    
权限:判断访问的用户是否能够访问某个API接口?
    默认全局权限控制:AllowAny
    修改全局权限控制
    修改指定视图权限控制

1
2
3
4
5
6
7
8
9
10
DRF框架其他功能-自定义权限控制类
如需自定义权限,需继承rest_framework.permissions.BasePermission父类,并实现以下两个任何一个方法或全部

.has_permission(self, request, view)

是否可以访问视图, view表示当前视图对象

.has_object_permission(self, request, view, obj)

是否可以访问数据对象, view表示当前视图, obj为数据对象

例如:

class MyPermission(BasePermission):
    def has_permission(self, request, view):
        """判断对使用此权限类的视图是否有访问权限"""
        # 任何用户对使用此权限类的视图都没有访问权限
        return True

    def has_object_permission(self, request, view, obj):
        """判断对使用此权限类视图某个数据对象是否有访问权限"""
        # 需求: 对id为1,3的数据对象有访问权限
        if obj.id in (1, 3):
            return True
        return False

class BookInfoViewSet(ReadOnlyModelViewSet):
    # 指定当前视图所使用的查询集
    queryset = BookInfo.objects.all()
    # 指定当前视图所使用的序列化器类
    serializer_class = BookInfoSerializer
    # 指定当前视图所使用的认证类
    authentication_classes = [SessionAuthentication]
    # 使用自定义的权限控制类
    permission_classes = [MyPermission]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
DRF框架其他功能-限流
限流:控制用户访问API接口频次。
    DRF框架默认没有进行限流设置。
    进行权限限流设置:
        1. 针对匿名用户和认证用户分别进行限流
        2. 针对匿名用户和认证用户统一进行限流

1
2
3
4
5
6
DRF框架其他功能-过滤&排序
   需求:
   写一个类视图,提供一个API接口
   1. 获取所有的图书 GET /books/
1
2
3
5.DRF框架其他功能-分页&自定义分页
1) PageNumberPagination

前端访问网址形式:

GET  http://api.example.org/books/?page=4
1
可以在子类中定义的属性:

page_size 每页数目
page_query_param 前端发送的页数关键字名,默认为"page"
page_size_query_param 前端发送的每页数目关键字名,默认为None
max_page_size 前端最多能设置的每页数量
2)LimitOffsetPagination

前端访问网址形式:

GET http://api.example.org/books/?limit=100&offset=400
1
可以在子类中定义的属性:

default_limit 默认限制,默认值与PAGE_SIZE设置一直
limit_query_param limit参数名,默认’limit’
offset_query_param offset参数名,默认’offset’
max_limit 最大limit限制,默认None
注意:如果在视图内关闭分页功能,只需在视图内设置

pagination_class = None
1
自定义分页类
也可通过自定义Pagination类,来为视图添加不同分页行为。在视图中通过pagination_clas属性来指明。

class StandardResultPagination(PageNumberPagination):
    page_size = 3
    page_size_query_param = 'page_size'
    max_page_size = 5
class BookListView(ListAPIView):
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer
    # 指定当前视图所使用的分页类
    pagination_class = StandardResultPagination
1
2
3
4
5
6
7
8
9
通过 http://api.example.org/books/?page=<页码>&page_size=<页容量> 进行访问。

DRF框架其他功能-异常处理

自定义DRF框架异常处理:
1. 自定义异常处理函数
2. 修改EXCEPTION_HANDLER配置项
1
2
3
DRF框架其他功能-自动生成接口文档

点赞 1
收藏
分享
  
zyj1189
发布了35 篇原创文章 · 获赞 16 · 访问量 1万+
私信
关注
DRF框架总结
阅读数 1993

序列化器类类名                                                                   Serializer  1)如果定义的序列化器类不是...
博文
来自:    我叫小王呀
 

DRF框架学习的小结
阅读数 1390

DRF框架一.认识restful架构REST,即RepresentationalStateTransfer的缩写,我们一般叫他'表现层状态转化'REST的路径设计思路是简洁:资源(比如HTML,或者图...
博文
来自:    qq_39801390的博客
DRF 框架学习小结
阅读数 574

前言:django restful framework框架,继续学习中。发现了一篇不错的文章。1、 RESTful是一种API的命名风格。2、 前后端分离: 用户访问静态文件的服务器,数据全部由aja...
博文
来自:    u012762054的博客
DRF算法
阅读数 22

背景在Mesos和YARN中,都用到了dominant resource fairness算法(DRF),它不同于hadoop基于slot-based实现的fair scheduler和capacit...
博文
来自:    架构设计
Django框架知识要点总结笔记
阅读数 273

哪些请求方式在请求地址的时候可以带请求体?只有PATCH、POST、PUT、DELETE访问一个url地址时可以带请求体web框架和web服务器web框架:flask Django作用:1.路由分发(...
博文
来自:    monkey的博客
drf技术扩展
阅读数 245

一.技术点xadmin管理后台国际化可视化apiapi文档管理二.安装pip install djangorestframeworkpip install markdown# 图片处理pip inst...
博文
来自:    宅神的博客
DRF简单整理总结
阅读数 158

1 序列反序列1序列化器1 serializers.Serializer用来给前端返回数据Serializer(instance=None, data=empty, **kwarg)1)用于序列化时,...
博文
来自:    非空盒子的博客
drf中如何同时实现create方法必填创建和patch方法部分更新创建?
阅读数 570

需求:models模型类字段依据创建需求,已经设定好了哪些字段为必填和非必填,而这些字段限制是根据创建设定的,如果想部分更新呢?用patch方法,具体操作如下:User用户表create:accoun...
博文
来自:    zhu6201976的博客
Django DRF model 多对多关系使用示例
阅读数 114

本例环境:Django=2.0.6 djangorestframework=3.9.21、模型示例from django.db import modelsclass BaseModel(models....
博文
来自:    haeasringnar的博客
drf常用方法
阅读数 7

1.认证2.权限3.序列化4.分页5.限流
博文
来自:    小明同学的博客
DRF框架知识点 - weixin_44033012的博客 - CSDN博客
11-27

Django框架知识要点总结笔记 - monkey的博客 - CSDN博客
11-11

DRF框架小结
阅读数 237

Python web框架要点web 应用程序的本质:1、接收并解析HTTP请求,获取具体的请求信息2、处理本次的HTTP请求,即完成本次请求的业务逻辑处理    3、构造并返回处理结果 —— HTTP响应D...
博文
来自:    weixin_42260623的博客
 关注
我叫小王呀
5篇文章

排名:千里之外

关注
布灵布灵、
2篇文章

排名:千里之外

关注
zhang_shuaixiang
101篇文章

排名:千里之外

关注
iteye_13202
0篇文章

排名:千里之外

DRF 框架学习小结 - u012762054的博客 - CSDN博客
10-4

DRF框架总结 - 我叫小王呀 - CSDN博客
11-11

一声令下即可关灯,再也不用在寒冬深夜离开被窝~
阅读数 3744

全文共2951字,预计学习时长6分钟你有没有在大冬天里的深夜里,为了关灯睡觉而不得不离开温暖被窝的经历?本文将介绍如何为普通家庭照明开关构建自然语言接口,以便用户可以使用如“请打开所有灯”或者“打开孩...
博文
来自:    读芯术的博客
不要在网站上无限滚动!
阅读数 1万+

人们在浏览网站的时候是喜欢用“无限滚动”,还是喜欢点击“阅读更多”或“查看更多”?无限滚动消除了分页的需要——分页是将数字内容分离到不同页面的过程。但这种方式真的好吗?作者|Monishreddy译者...
博文
来自:    CSDN资讯
DRF总结 - qq_44291044的博客 - CSDN博客
8-23

Django框架知识点简单总结(1) - weixin_30737363的博客 - CSDN博客
10-5

学Linux到底学什么
阅读数 2万+

来源:公众号【编程珠玑】作者:守望先生网站:https://www.yanbinghu.com/2019/09/25/14472.html前言​我们常常听到很多人说要学学Linux或者被人告知说应该学...
博文
来自:    守望的博客-编程珠玑
规模比互联网大 30 倍的物联网,入门太难了!
阅读数 186

近几年来,物联网发展迅速:据中商产业研究院《2016——2021年中国物联网产业市场研究报告》显示,预计到2020年,中国物联网的整体规模将达2.2万亿元,产业规模比互联......
博文
来自:    程序人生的博客
drf框架--基础 - weixin_30472035的博客 - CSDN博客
10-3

DRF框架 - weixin_41449756的博客 - CSDN博客
11-20

drf系列总结目录
阅读数 12

啊,我真的是被坑了,可能没注意到下面的配置吧,一直没有写这个CNAME,导致一直出现404页面解决方法在hexo下面的source文件夹中写一个文件,CNAME,内容写你自己的url地址。过一会儿就会...
博文
来自:    小人物的博客
DRF框架的一些概述总结
阅读数 1859

博文
来自:    sober333的博客
DRF 框架总结 - 引入 Django REST framework 框架
阅读数 62

引入 Django REST framework 框架Web 应用模式在开发 Web 应用中,有两种开发模式:前后端不分离前后端分离前后端不分离在前后端不分离的应用模式中,前端看到的效果都是有后端控制...
博文
来自:    qyf__123的博客
DRF 框架总结 - 版本 Versioning
阅读数 84

版本VersioningREST framework提供了版本号的支持。在需要获取请求的版本号时,可以通过request.version来获取。默认版本功能未开启,request.version 返回...
博文
来自:    qyf__123的博客
DRF框架视图集使用
阅读数 124

一、Django REST framework 简介Django REST framework 框架是一个用于构建Web API 的强大而又灵活的工具。通常简称为DRF框架 或 REST framew...
博文
来自:    菲宇运维
Django REST framework API 指南(25):状态码
阅读数 140

官方原文链接本系列文章 github 地址转载请注明出处状态码不建议在你的响应中使用裸露(直接使用数字)的状态码。 REST framework 包含一组命名常量,你可以使用它们使代码更加清晰易读。f...
博文
来自:    weixin_34032792的博客
接私活必备的 10 个开源项目!
阅读数 7万+

点击蓝色“GitHubDaily”关注我加个“星标”,每天下午18:35,带你逛GitHub!作者|SevDot来源|http://1t.click/VE8W......
博文
来自:    GitHubDaily
DRF框架知识点
阅读数 30

Web开发两种模式前后端不分离前端看到的效果是由后端进行控制的,后端进行模板渲染,返回渲染之后完整页面。前后端分离:后端只返回前端所需的数据,至于数据的展示,由前端自己进行控制。JsonRespons...
博文
来自:    weixin_44033012的博客
技术一旦被用来作恶,究竟会有多可怕?
阅读数 1万+

技术一直都在被用来作恶。作为与经常与黑客、攻击者打交道的我们,熟知各种用技术作恶的手段。这篇就作为简单的科普文来跟大家讲一讲。作恶之一:DDoS攻击用简单的一句话介绍DDoS攻击就是:黑客在短时间里发...
博文
来自:    知道创宇KCSC
**DRF框架基本使用**
阅读数 356

DRF框架基础定义模型类from django.db import modelsclass BookInfo(models.Model):“”“模型类”&amp;quot;&amp;quot;# 类属...
博文
来自:    Mr_WoLong
python笔记(restframework 视图,视图总结)
阅读数 30

APIView class BookView(APIView): pass url(r'^books/$', views.BookView.as_view(),name="books"), 分析走的...
博文
来自:    小小龙的博客
Django drf用户配置
阅读数 72

REST_FRAMEWORK = { # 'DEFAULT_AUTHENTICATION_CLASSES': ['apps.api.utils.auth.FirstAuthtication', ...
博文
来自:    Ginta的博客
DRF五大接口
阅读数 140

面向对象封装面向对象封装导入# 面向对象的封装:# 1.将子类共有的方法抽离形成父类# 2.子类使用共有方法,使用的是父类方法# 3.共有方法中的资源,在子类使用方法时,获取的是子类资源class M...
博文
来自:    qq_36811322的博客
DRF框架常见坑
阅读数 143

反序列化错误反序列化校验错误有一些参数是不需要反序列化的。如果这些参数也被反序列化了,那么程序会报错,报错内容如下:报错提示:请求方法不被允许。这个时候要检查是否存在不需要被反序列化的字段被反序列化了...
博文
来自:    qq_42799459的博客
DRF总结(一)--图解各个View之间的关系
阅读数 143

图解各个View之间的关系1.各个View之间的关系1.1 首先弄懂CBV和FBVFBV模式(Function Base View)CBV模式(Class Base View)1.2 view的执行流...
博文
来自:    huotong
推荐一位好朋友 | 机械转大数据,并拿到66个offer
阅读数 1769

锦锋是我的好朋友,他自学编程从车辆工程转到了Java开发然后转大数据,在校招中拿了大小厂66个offer,其中有头条、腾讯等。除了学习之外,他还是国家高级健身教练,同时也......
博文
来自:    u010459192的博客
DRF 框架总结 - 限流 Throttling
阅读数 326

限流Throttling可以对接口访问的频次进行限制,以减轻服务器压力。使用可以在配置文件中,使用DEFAULT_THROTTLE_CLASSES 和 DEFAULT_THROTTLE_RATES进行...
博文
来自:    qyf__123的博客
网络路由知识大全
阅读数 282

1、如果ping域名的时候出现ping:unknown host xxx.xxx但是ping IP地址的时候可以通的话可知是dns服务器没有配置好,查看一下配置文件/etc/resolv.conf,...
博文
来自:    $好记性还是要多记录$
DRF总结(二)View中各个配置的使用
阅读数 302

DRF总结(二)View中各个配置的使用常用的属性,以以下代码为例查询结果集分页过滤搜索使用Drf进行search_filter排序使用DRF进行ordering_filter配置serializer...
博文
来自:    huotong
Django REST framework的使用总结
阅读数 41

在序列化与反序列化时,虽然操作的数据不尽相同,但是执行的过程却是相似的,也就是说这部分代码是可以复用简化编写的;在开发REST API的视图中,虽然每个视图具体操作的数据不同,但增、删、改、查的实现流...
博文
来自:    weixin_33943347的博客
DRF 从入门到精通
阅读数 65

课程概述这是一份错过可惜的DRF专注教程!这是一份稀缺的教程:全网独有,专注Django REST framework本身,不挂羊头卖狗肉。这是一份最新的教程:基于Python3.7 + Django...
博文
来自:    QQQ1791198045的博客
DRF总结
阅读数 30

DRF组件:版本权限认证节流分页解析器序列化路由视图渲染器前后端分离为前端提供统一接口restful规范:http://127.0.0.1:8000/users/前端vue跨域浏览器的同源策略如何理解...
博文
来自:    qq_44291044的博客
[Python][Django][DRF]框架序列化器知识点汇总思维导图
阅读数 88

近期学习django的rest_framework, 学完重点序列化器之后为了更好的巩固自身也为了能够帮到大家, 花了两个小时总结整理出了一份思维导图,希望能对大家学习或者复习起到帮助作用思维导图工具...
博文
来自:    wuuud1的博客
drf 框架
阅读数 9

一. drf简介   drf框架,全程: django-rest framework , rest是插件名字,django插件的名字叫rest,framework是框架的意思 二. 接口   在平时生...
博文
来自:    技术研究中心
DRF 框架总结 - 自动生成接口文档
阅读数 363

自动生成接口文档REST framework可以自动帮助我们生成接口文档。接口文档以网页的方式呈现。自动接口文档能生成的是继承自APIView及其子类的视图。1. 安装依赖REST framewrok...
博文
来自:    qyf__123的博客
史上最详细的IDEA优雅整合Maven+SSM框架(详细思路+附带源码)
阅读数 6万+

网上很多整合SSM博客文章并不能让初探ssm的同学思路完全的清晰,可以试着关掉整合教程,摇两下头骨,哈一大口气,就在万事具备的时候,开整,这个时候你可能思路全无 ~中招了咩~ ,还有一些同学依旧在使用...
博文
爬虫小程序 - 爬取王者荣耀全皮肤
阅读数 1万+

王者荣耀全皮肤图片爬取
博文
从入门到精通,Java学习路线导航
阅读数 8万+

引言 最近也有很多人来向我"请教",他们大都是一些刚入门的新手,还不了解这个行业,也不知道从何学起,开始的时候非常迷茫,实在是每天回复很多人也很麻烦,所以在这里统一作个回复吧。 Java学习路线 当然...
博文
如何优雅的爬妹子网
阅读数 2万+

from urllib import request import os from user_agents import ua_list import time import random impor...
博文
花了20分钟,给女朋友们写了一个web版群聊程序
阅读数 2万+

参考博客 [1]https://www.byteslounge.com/tutorials/java-ee-html5-websocket-example
博文
对计算机专业来说学历真的重要吗?
阅读数 13万+

我本科学校是渣渣二本,研究生学校是985,现在毕业五年,校招笔试、面试,社招面试参加了两年了,就我个人的经历来说下这个问题。 这篇文章很长,但绝对是精华,相信我,读完以后,你会知道学历不好的解决方案...
博文
Java入门学习路线目录索引(持续更新中)
阅读数 1万+

新增: Redis 入门 【Redis缓存】- 入门——Redis介绍和环境搭建【Redis缓存】- Redis数据结构、基本命令操作、持久化【Redis缓存】- Java客户端Jedis Sp...
博文
SpringBoot学习笔记三、http接口请求
阅读数 210

controller package com.example.demo.controller; import java.util.HashMap; import java.util.Map; ...
博文
程序员必须掌握的核心算法有哪些?
阅读数 8万+

由于我之前一直强调数据结构以及算法学习的重要性,所以就有一些读者经常问我,数据结构与算法应该要学习到哪个程度呢?,说实话,这个问题我不知道要怎么回答你,主要取决于你想学习到哪些程度,不过针对这个问题,...
博文
Python——画一棵漂亮的樱花树(不同种樱花+玫瑰+圣诞树喔)
阅读数 6万+

最近翻到一篇知乎,上面有不少用Python(大多是turtle库)绘制的树图,感觉很漂亮,我整理了一下,挑了一些我觉得不错的代码分享给大家(这些我都测试过,确实可以生成) one 樱花树 动...
博文
这应该是把计算机网络五层模型讲的最好是文章了,看不懂你打我
阅读数 3万+

帅地:用心写好每一篇文章! 前言 天各一方的两台计算机是如何通信的呢?在成千上万的计算机中,为什么一台计算机能够准确着寻找到另外一台计算机,并且把数据发送给它呢? 可能很多人都听说过网络通信的 5 ...
博文
HTML CSS整理笔记
阅读数 2万+

常见字体单位: 1.em 移动端常用的字体尺寸单位,说白em就相当于“倍”,比如设置当前的div的字体大小为1.5em,则当前的div的字体大小为:当前div继承的字体大小*1.5。 但当div进行嵌...
博文
史上最全的mysql基础教程
阅读数 3万+

启动与停止 启动mysql服务 sudo /usr/local/mysql/support-files/mysql.server start 停止mysql服务 sudo /usr/loc...
博文
有哪些让程序员受益终生的建议
阅读数 7万+

从业五年多,辗转两个大厂,出过书,创过业,从技术小白成长为基层管理,联合几个业内大牛回答下这个问题,希望能帮到大家,记得帮我点赞哦。 敲黑板!!!读了这篇文章,你将知道如何才能进大厂,如何实现财务自...
博文
大学四年自学走来,这些私藏的实用工具/学习网站我贡献出来了
阅读数 16万+

大学四年,看课本是不可能一直看课本的了,对于学习,特别是自学,善于搜索网上的一些资源来辅助,还是非常有必要的,下面我就把这几年私藏的各种资源,网站贡献出来给你们。主要有:电子书搜索、实用工具、在线视频...
博文
linux系列之常用运维命令整理笔录
阅读数 1万+

本博客记录工作中需要的linux运维命令,大学时候开始接触linux,会一些基本操作,可是都没有整理起来,加上是做开发,不做运维,有些命令忘记了,所以现在整理成博客,当然vi,文件操作等就不介绍了,慢...
博文
大学四年,我把私藏的自学「学习网站/实用工具」都贡献出来了
阅读数 9万+

在分享之前,先说说初学者如何学习编程,这个话题想必非常的重要,要学好编程,给你一些学习网站也好、实用工具也好,但前提是你知道如何去学习它。 见过很多初学者,以及小鹿我刚开始学习的时候,也是自己瞎摸索,...
博文
中国麻将:世界上最早的区块链项目
阅读数 5万+

中国麻将:世界上最早的区块链项目 最近区块链这个玩意又被市场搞的很是火热,相信大部分人都不太清楚这玩意到底是怎么样的一个概念,它来了,它来了,它到底是啥~ 国家都开始发文支持了,下面是一个通俗易懂的...
博文
比特币原理详解
阅读数 4万+

一、什么是比特币 比特币是一种电子货币,是一种基于密码学的货币,在2008年11月1日由中本聪发表比特币白皮书,文中提出了一种去中心化的电子记账系统,我们平时的电子现金是银行来记账,因为银行的背后是...
博文
Python 基础(一):入门必备知识
阅读数 2万+

Python 入门必备知识,你都掌握了吗?
博文
兼职程序员一般可以从什么平台接私活?
阅读数 8万+

这个问题我进行了系统性的总结,以下将进行言简意赅的说明和渠道提供,希望对各位小猿/小媛们有帮助~ 根据我们的经验,程序员兼职主要分为三种:兼职职位众包、项目整包和自由职业者驻场。 所谓的兼职职位众...
博文
Ngrok: 超简单的内网穿透,了解一下 ?
阅读数 1万+

【1】什么是内网穿透? 首先,我们生活中的网络从应用上可以分为内网和外网; 内网就是你自己的网络环境,就你自己能访问,比如你本地测试进行的localhost; 外网就不言而喻了,你看网页,视频等...
博文
反射全解
阅读数 1万+

反射的概念 反射的引入: Object obj = new Student(); 若程序运行时接收到外部传入的一个对象,该对象的编译类型是Object,但程序又需要调用该对象运行类型的方法: ...
博文
图解面试题:如何提高SQL查询的效率?
阅读数 9768

【题目】我们公司的数据量非常大,需要的不仅仅是提取数据,要了解SQL方案优化的。一般在写SQL时需要注意哪些问题,可以提高查询的效率?【解题思路】数据量大的情况下,不同的SQL语句,消耗的时间相差很大...
博文
死磕C语言指针
阅读数 1万+

兜兜转转还是逃不过 C 语言,这该死的缘分。 先看一眼我的西野七濑 学习自:https://zhuanlan.zhihu.com/p/89121683 目录 1 指针 1.1 指针是乜嘢...
博文
Python十大装B语法
阅读数 8万+

Python 是一种代表简单思想的语言,其语法相对简单,很容易上手。不过,如果就此小视 Python 语法的精妙和深邃,那就大错特错了。本文精心筛选了最能展现 Python 语法之精妙的十个知识点,并...
博文
数据库优化 - SQL优化
阅读数 1万+

从一个示例入手,带你一步一步掌握SQL优化的技巧!
博文

————————————————
版权声明:本文为CSDN博主「zyj1189」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_44143222/article/details/88878072

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!