安装
| pip3 install djangorestframework |
Restful规范
| |
| |
| |
| |
| |
| -1 保证数据安全,通常使用https进行交互(https比http安全,http+ssl=https) |
| -2 接口中带,api关键字标,识接口api接口 |
| https://api.baidu.com |
| https://127.0.0.1/api/books |
| |
| -3 url链接中带接口版本号 |
| https://api.baidu.com/v1 |
| https://api.baidu.com/v2 |
| |
| -4 数据即是资源,均使用名词(可复数)(地址中不带操作资源的动词) |
| https://api.baidu.com/v1/books |
| -5 资源操作由请求方式决定 |
| -获取数据 get |
| -新增数据 post |
| -删除数据 delete |
| -修改数据 put/patch |
| |
| -6 在url中带搜索,过滤,排序条件 |
| https://api.example.com/v1/zoos?limit=10 |
| -7 响应中带状态码 |
| -http响应,就有状态码 |
| -2xx |
| -3xx |
| -4xx |
| -5xx |
| -200,301,302,403,404,500 |
| -返回的json格式中也带状态码 |
| -公司自己定制的 |
| {code:1000} |
| |
| -8 响应的json格式中带错误信息 |
| {code:1000,msg:成功} |
| |
| -9 针对不同操作,服务器向用户返回的结果应该符合以下规范 |
| GET /books:返回资源对象的列表(数组) |
| GET /books/1:返回单个资源对象 |
| POST /books:返回新生成的资源对象 |
| PUT /books/2:返回完整的资源对象 (完全修改) |
| PATCH /books/2:返回完整的资源对象(局部修改) |
| DELETE /books/1:返回一个空文档 |
| -10 响应中带链接 |
| |
序列化
| |
| 序列化: 把我们识别的数据转换成指定的格式提供给别人 |
| 反序列化:把别人提供的数据转换/还原成我们需要的格式。 |
常用字段
| |
| ''' |
| CharField |
| IntegerField |
| DecimalField |
| DateTimeField |
| DateField |
| |
| ListField |
| DictField |
| ''' |
序列化类常用字段参数
| |
| read_only 表明该字段仅用于序列化输出,默认False(重点) |
| write_only 表明该字段仅用于反序列化输入,默认False(重点) |
| required 表明该字段在反序列化时必须输入,默认True(了解) |
| default 反序列化时使用的默认值(了解) |
| allow_null 表明该字段是否允许传入None,默认False(了解) |
| validators 该字段使用的验证器(写函数的列表,使用这些函数校验该字段)(了解) |
| error_messages 包含错误编号与错误信息的字典 |
| |
| |
| |
| |
| max_length 最大长度 |
| min_lenght 最小长度 |
| allow_blank 是否允许为空 |
| trim_whitespace 是否截断空白字符 |
| |
| |
| max_value 最小值 |
| min_value 最大值 |
反序列化
| (重写create和update) |
| |
| -如果想序列化和反序列化都用一个序列化类,可以使用如下俩字段控制 |
| -read_only 表明该字段仅用于序列化输出,默认False(重点) |
| -write_only 表明该字段仅用于反序列化输入,默认False(重点) |
| -如果写起来比较麻烦,可以使用两个序列化类,一个序列化,一个反序列化 |
| |
| name1 = serializers.CharField(source='name')--->意思是name1映射成models中的name |
| |
| |
| |
| BookSerializer---》Serializer---》BaseSerializer--save的核心代码 |
| if self.instance is not None: |
| self.instance = self.update(self.instance, validated_data) |
| else: |
| self.instance = self.create(validated_data) |
| |
| |
| |
| |
SerializerMethodField的使用
| class BookSerializer(serializers.Serializer): |
| price = serializers.IntegerField() |
| |
| name = serializers.CharField() |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| publish_detail = serializers.CharField(source='publish.addr') |
序列化类之ModelSerializer
| from .models import Book, Publish |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
序列化类全局,局部钩子
| |
| def validate_addr(self, item): |
| if item.startswith('sb'): |
| |
| raise ValidationError('不能以sb开头') |
| else: |
| return item |
| |
| |
| |
| def validate(self, item): |
| name = attrs.get('name') |
| addr = attrs.get('addr') |
| if name == addr: |
| raise ValidationError('name和addr不能一样') |
| else: |
| return attrs |
序列化类源码分析
| |
| -ser.is_valid--->BaseSerializer的is_valid---》self._validated_data = self.run_validation(self.initial_data)---》是哪个类的run_validation?不是Field类的,而是Serializer类的 |
| |
| -Serializer类 |
| -run_validation内部有一句 |
| |
| -value = self.to_internal_value(data) |
| for field in fields: |
| validate_method = getattr(self, 'validate_' + field.field_name, None) |
| validated_value = field.run_validation(primitive_value) |
| if validate_method is not None: |
| validated_value = validate_method(validated_value) |
| -value = self.validate(value) |
drf的请求
| |
| -.data:包含了对POST、PUT、PATCH请求方式解析后的数据 |
| -.query_params:Django标准的request.GET |
| |
| |
| - 默认情况支持三种 |
| -全局配置(在项目的settings.py中) |
| REST_FRAMEWORK = { |
| 'DEFAULT_PARSER_CLASSES': [ |
| 'rest_framework.parsers.JSONParser', |
| |
| |
| ] |
| } |
| -局部配置(在视图类中写类属性) |
| class IndexView(APIView): |
| parser_classes = [JSONParser,] |
| |
| |
| |
| |
| |
| |
| |
| |
| 'DEFAULT_PARSER_CLASSES': [ |
| 'rest_framework.parsers.JSONParser', |
| 'rest_framework.parsers.FormParser', |
| 'rest_framework.parsers.MultiPartParser' |
| ] |
响应
| |
| |
| -data:要返回的数据,放到了http响应的响应体中 |
| -status:http响应的状态码 |
| -drf把所有的状态码都定义成了常量 |
| -rest_framework.status下 |
| -headers http响应头,是个字典 |
| {'name':'lqz'} |
| -content_type响应编码格式,了解 |
| |
| |
| |
| -全局配置(在项目的settings.py中) |
| REST_FRAMEWORK = { |
| 'DEFAULT_RENDERER_CLASSES': [ |
| 'rest_framework.renderers.JSONRenderer', |
| 'rest_framework.renderers.BrowsableAPIRenderer', |
| ] |
| } |
| -局部配置(在视图类中写类属性) |
| class IndexView(APIView): |
| renderer_classes = [JSONOpenAPIRenderer,] |
视图组件之两个视图基类
APIView
| |
| class BookView(APIView): |
| def get(self, request): |
| qs = Book.objects.all() |
| ser = serializer.BookSerializer(instance=qs, many=True) |
| return Response(data=ser.data, status=200) |
| |
| def post(self, request): |
| ser = serializer.BookSerializer(data=request.data) |
| if ser.is_valid(): |
| ser.save() |
| return Response(data={'code': 100, 'msg': '创建成功'}) |
| else: |
| return Response(data=ser.errors) |
| |
| class BookDetailView(APIView): |
| def get(self, request, pk): |
| book = Book.objects.all().filter(pk=pk).first() |
| ser = serializer.BookSerializer(instance=book) |
| return Response(data=ser.data, status=200) |
| |
| def put(self, request, pk): |
| book = Book.objects.all().filter(pk=pk).first() |
| ser = serializer.BookSerializer(data=request.data, instance=book) |
| if ser.is_valid(): |
| ser.save() |
| return Response(data={'code': 100, 'msg': '修改成功'}) |
| else: |
| return Response(data=ser.errors) |
| |
| def delete(self, request, pk): |
| |
| Book.objects.filter(pk=pk).delete() |
| return Response(data={'code': 100, 'msg': '删除成功'}) |
GenericAPIView
| |
| ''' |
| 有两个类属性 |
| queryset = Book.objects.all() # 你要序列化的数据 |
| serializer_class = serializer.BookSerializer # 你要使用的序列化类 |
| 三个方法 |
| get_queryset() # 获取要序列化的数据 |
| get_serializer() # 获取要使用的序列化类 |
| get_object() # 获取单条数据(一定要用pk,如果你想改,重写类属性lookup_field) |
| |
| ''' |
| from rest_framework.generics import GenericAPIView |
| |
| |
| class BookView(GenericAPIView): |
| queryset = Book.objects.all() |
| serializer_class = serializer.BookSerializer |
| |
| def get(self, request): |
| qs = self.get_queryset() |
| ser = self.get_serializer(instance=qs, many=True) |
| return Response(data=ser.data, status=200) |
| |
| def post(self, request): |
| ser = self.get_serializer(data=request.data) |
| if ser.is_valid(): |
| ser.save() |
| return Response(data={'code': 100, 'msg': '创建成功'}) |
| else: |
| return Response(data=ser.errors) |
| |
| |
| class BookDetailView(GenericAPIView): |
| queryset = Book.objects.all() |
| serializer_class = serializer.BookSerializer |
| |
| def get(self, request, *args, **kwargs): |
| book = self.get_object() |
| ser = self.get_serializer(instance=book) |
| return Response(data=ser.data, status=200) |
| |
| def put(self, request, pk): |
| book = self.get_object() |
| ser = self.get_serializer(data=request.data, instance=book) |
| if ser.is_valid(): |
| ser.save() |
| return Response(data={'code': 100, 'msg': '修改成功'}) |
| else: |
| return Response(data=ser.errors) |
| |
| def delete(self, request, pk): |
| |
| self.get_object().delete() |
| |
| return Response(data={'code': 100, 'msg': '删除成功'}) |
视图组件之5个视图扩展类
http://127.0.0.1:8000/media/image/avatar.jpg
| |
| from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin |
| |
| from rest_framework.generics import GenericViewSet |
| |
| |
| |
| |
| |
| |
| |
| class BookView(GenericViewSet,ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin): |
| queryset = Book.objects.all() |
| serializer_class = serializer.BookSerializer |
| lookup_field="id" |
| |
| |
| |
| |
| |
| |
| class BookView(GenericViewSet,ListModelMixin,CreateModelMixin): |
| queryset = Book.objects.all() |
| serializer_class = serializer.BookSerializer |
| |
| def get(self, request): |
| |
| |
| |
| return self.list(request) |
| |
| def post(self, request): |
| return self.create(request) |
| |
| |
| class BookDetailView(GenericViewSet,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin): |
| queryset = Book.objects.all() |
| serializer_class = serializer.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) |
视图组件之9个视图子类
| |
| from rest_framework.generics import ListAPIView, CreateAPIView, ListCreateAPIView |
| from rest_framework.generics import RetrieveAPIView, UpdateAPIView, DestroyAPIView, RetrieveUpdateAPIView, \ |
| RetrieveDestroyAPIView, RetrieveUpdateDestroyAPIView |
| |
| |
| |
| |
| class BookView(ListCreateAPIView): |
| queryset = Book.objects.all() |
| serializer_class = serializer.BookSerializer |
| |
| |
| class BookDetailView(RetrieveUpdateDestroyAPIView): |
| queryset = Book.objects.all() |
| serializer_class = serializer.BookSerializer |
视图组件之视图集
| |
| from rest_framework.viewsets import ModelViewSet |
| class BookView(ModelViewSet): |
| queryset = Book.objects.all() |
| serializer_class = serializer.BookSerializer |
drf之路由
基本使用
| |
| |
| |
| from rest_framework.routers import DefaultRouter, SimpleRouter |
| |
| |
| router = SimpleRouter() |
| |
| |
| |
| |
| |
| |
| router.register('test', views.TestView, basename='testview') |
| |
| print(router.urls) |
| |
| urlpatterns = [ |
| path('admin/', admin.site.urls), |
| path('index/', views.index), |
| |
| |
| path('', include(router.urls)), |
| |
| ] |
| |
| |
actions
| |
| class TestView(GenericViewSet): |
| '''action参数 |
| methods:做一个映射,就是http的请求方式,就会映射当前方法 |
| detail:True或False |
| -test/login/ |
| -^test/pk/login |
| url_path:访问的路径,可以不写,如果不写,以方法名作为路径 通过get请求访问这个路径test/login就能触发login的执行 |
| url_name:别名 |
| ''' |
| queryset = Book.objects.all() |
| serializer_class = BookSerializer |
| |
| def get_serializer_class(self): |
| |
| if self.action=='login': |
| return PublishSerializer |
| else: |
| return self.serializer_class |
| |
| |
| @action(methods=['GET'],detail=True) |
| def login(self, request,pk): |
| print(self.action) |
| print(pk) |
| |
| return Response('登录成功') |
| |
| |
| |
| router.register('test', views.TestView, basename='testview') |
drf之认证功能
| 判断用户是否登录 |
| 某个接口登录后才能访问,需要使用认证功能 |
登录功能
| |
| class LoginView(ViewSet): |
| authentication_classes = [] |
| @action(methods=['POST'], detail=False) |
| def login(self, request): |
| name = request.data.get('name') |
| password = request.data.get('password') |
| user=User.objects.filter(name=name,password=password).first() |
| if user: |
| |
| |
| token=str(uuid.uuid4()) |
| |
| |
| UserToken.objects.update_or_create(defaults={'token':token},user=user) |
| return Response({'code':100,'msg':'登录成功','token':token}) |
| else: |
| return Response({'code':101,'msg':'用户名或密码错误'}) |
认证类
| |
| from rest_framework.authentication import BaseAuthentication |
| from rest_framework.exceptions import AuthenticationFailed |
| from .models import UserToken |
| |
| |
| class LoginAuth(BaseAuthentication): |
| def authenticate(self, request): |
| |
| token = request.GET.get('token') |
| |
| user_token = UserToken.objects.filter(token=token).first() |
| if user_token: |
| |
| |
| return user_token.user, token |
| else: |
| raise AuthenticationFailed('token不合法或没有迭代token') |
认证类的使用
| |
| |
| |
| from app01.auth import LoginAuth |
| class PublishView(ModelViewSet): |
| |
| authentication_classes = [LoginAuth,] |
| |
| |
| |
| |
| REST_FRAMEWORK={ |
| "DEFAULT_AUTHENTICATION_CLASSES":["app01.auth.LoginAuth",] |
| } |
JWT
介绍
| |
| |
| |
| |
| |
| |
| -三段式: |
| -header:一般放公司信息,加密方式 (用处不大) |
| -payload:用户信息: user_id,user_name,email,expire |
| -signature:第一部分和第二部加密得到的 |
| -通过base64转码 |
| eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ |
快速使用
| |
| |
| |
| |
| |
| |
| |
| |
| -用户表使用auth的user表 |
| -登录功能不用写,djangorestframework-jwt帮你写好了 |
内置认证类使用
签发
| |
| from rest_framework_jwt.views import obtain_jwt_token |
| urlpatterns = [ |
| path('login/', obtain_jwt_token), |
| ] |
认证
| from rest_framework_jwt.authentication import JSONWebTokenAuthentication |
| from rest_framework.permissions import IsAuthenticated |
| class BookView(APIView): |
| |
| authentication_classes = [JSONWebTokenAuthentication, ] |
| permission_classes = [IsAuthenticated] |
自定义token签发认证
| from rest_framework.response import Response |
| from .models import UserInfo |
| from rest_framework_jwt.settings import api_settings |
| |
| jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER |
| jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER |
| |
| |
| class LoginView(APIView): |
| def post(self, request): |
| username = request.data.get('username') |
| password = request.data.get('password') |
| user = UserInfo.objects.filter(username=username, password=password).first() |
| if user: |
| payload = jwt_payload_handler(user) |
| token = jwt_encode_handler(payload) |
| return Response({'code': '100', 'msg': '登录成功', 'token': token, 'usrename': user.username}) |
| else: |
| return Response({'code': '101', 'msg': '用户名或密码错误'}) |
| |
| |
| |
| |
| from rest_framework.authentication import BaseAuthentication |
| from rest_framework import exceptions |
| import jwt |
| from .models import UserInfo |
| from rest_framework_jwt.settings import api_settings |
| jwt_decode_handler = api_settings.JWT_DECODE_HANDLER |
| class JwtAuthentication(BaseAuthentication): |
| def authenticate(self, request): |
| |
| token=request.META.get('HTTP_TOKEN') |
| |
| try: |
| payload = jwt_decode_handler(token) |
| |
| |
| except jwt.ExpiredSignature: |
| msg = '签名过期' |
| raise exceptions.AuthenticationFailed(msg) |
| except jwt.DecodeError: |
| msg ='解码错误' |
| raise exceptions.AuthenticationFailed(msg) |
| except jwt.InvalidTokenError: |
| raise exceptions.AuthenticationFailed() |
| |
| |
| |
| |
| |
| |
| user=UserInfo(id=payload['user_id'],username=payload['username']) |
| |
| return user,token |
simplekjwt
settings.py
| REST_FRAMEWORK = { |
| 'DEFAULT_AUTHENTICATION_CLASSES': ( |
| 'rest_framework_simplejwt.authentication.JWTAuthentication', |
| ) |
| } |
| |
| INSTALLED_APPS = [ |
| ... |
| 'rest_framework_simplejwt' |
| ... |
| ] |
| |
views
| from rest_framework.permissions import IsAuthenticated |
| permission_classes = [IsAuthenticated] |
获取token
| from rest_framework_simplejwt.tokens import RefreshToken |
| def _get_tokens_for_user(self, user): |
| refresh = RefreshToken.for_user(user) |
| return str(refresh.access_token) |
验证
| 请求头 |
| key value |
| Authorization Bearer token值 |
案例
| |
| REST_FRAMEWORK = { |
| 'DEFAULT_AUTHENTICATION_CLASSES': ( |
| 'rest_framework_simplejwt.authentication.JWTAuthentication', |
| ) |
| } |
| |
| INSTALLED_APPS = [ |
| ... |
| 'rest_framework_simplejwt' |
| ... |
| ] |
| |
| |
| class UserViews(ViewSet): |
| @action(methods=['POST'], detail=False) |
| def login(self, request): |
| res = UserSerializer(data=request.data) |
| if res.is_valid(): |
| token = res.context.get('token') |
| user = res.context.get('user') |
| msg = { |
| "token": token, |
| "username": user.username |
| } |
| return Response({"code": "登录成功", "msg": msg}) |
| return Response(res.errors) |
| |
| |
| |
| from rest_framework import serializers |
| from rest_framework.exceptions import ValidationError |
| from rest_framework_simplejwt.tokens import RefreshToken |
| from . import models |
| |
| |
| class UserSerializer(serializers.ModelSerializer): |
| username = serializers.CharField(max_length=32) |
| |
| class Meta: |
| model = models.UserInfo |
| fields = ["username", "password"] |
| |
| def validate(self, attrs): |
| user = self._get_user(attrs) |
| self.context['user'] = user |
| self.context['token'] = self._get_tokens_for_user(user) |
| return attrs |
| |
| def _get_user(self, attrs): |
| username = attrs.get("username") |
| password = attrs.get("password") |
| user = models.UserInfo.objects.filter(username=username).first() |
| if user and user.check_password(password): |
| return user |
| else: |
| raise ValidationError({'detail': '用户名或密码错误'}) |
| |
| def _get_tokens_for_user(self, user): |
| refresh = RefreshToken.for_user(user) |
| |
| return str(refresh.access_token) |
| |
| |
| from rest_framework.permissions import IsAuthenticated |
| permission_classes = [IsAuthenticated] |
配置
| |
| from datetime import timedelta |
| |
| SIMPLE_JWT = { |
| 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5), |
| 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), |
| |
| 'ROTATE_REFRESH_TOKENS': False, |
| 'BLACKLIST_AFTER_ROTATION': True, |
| |
| |
| 'ALGORITHM': 'HS256', |
| 'SIGNING_KEY': SECRET_KEY, |
| 'VERIFYING_KEY': None, |
| 'AUDIENCE': None, |
| 'ISSUER': None, |
| |
| 'AUTH_HEADER_TYPES': ('Bearer',), |
| 'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION', |
| 'USER_ID_FIELD': 'id', |
| 'USER_ID_CLAIM': 'user_id', |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| } |
drf之权限
| |
| from rest_framework.permissions import BasePermission |
| |
| |
| class PermissionUser(BasePermission): |
| def has_permission(self, request, view): |
| |
| if request.user.user_type == 1: |
| return True |
| else: |
| return False |
局部和全局使用
| |
| from app01.auth import LoginAuth |
| class PublishView(ModelViewSet): |
| |
| permission_classes = [PermissionUser] |
| |
| |
| |
| REST_FRAMEWORK={ |
| "DEFAULT_PERMISSION_CLASSES":["app01.auth.PermissionUser",] |
| } |
频率限制
| |
| |
| -request.META.get('REMOTE_ADDR') |
| |
| |
| |
| |
| |
| |
| |
| REST_FRAMEWORK = { |
| 'DEFAULT_THROTTLE_RATES': { |
| |
| 'ip_m_3': '3/m', |
| } |
| } |
| |
| |
| |
| |
| -配置在视图类中 |
| class IndexView(APIView): |
| throttle_classes = [MyThrotting, ] |
| -配置在settings.py中 |
| REST_FRAMEWORK = { |
| 'DEFAULT_THROTTLE_CLASSES': ['app01.auth.MyThrotting',], |
| } |
| class MyThrotting(SimpleRateThrottle): |
| scope = 'ip_m_3' |
| |
| def get_cache_key(self, request, view): |
| return self.get_ident(request) |
过滤排序
内置 过滤使用
| from rest_framework.generics import ListAPIView |
| from .models import Book |
| from .serializer import BookSerializer |
| from rest_framework.viewsets import ViewSetMixin, GenericViewSet |
| from rest_framework.mixins import ListModelMixin |
| |
| |
| from rest_framework.filters import SearchFilter,OrderingFilter |
| class BookView(GenericViewSet, ListModelMixin): |
| queryset = Book.objects.all() |
| serializer_class = BookSerializer |
| |
| |
| filter_backends = [SearchFilter,] 可以有多个过滤类 |
| |
| search_fields=['name',] |
| search_fields=['name','price'] |
| |
| class SelfFilterBackend(BaseFilterBackend): |
| def filter_queryset(self, request, queryset, view): |
| return queryset.filter(mtb_user_id=request.user.user_id) |
内置排序使用
| from rest_framework.filters import SearchFilter, OrderingFilter |
| |
| |
| class BookView(GenericViewSet, ListModelMixin): |
| queryset = Book.objects.all() |
| serializer_class = BookSerializer |
| |
| |
| filter_backends = [OrderingFilter, ] |
| |
| |
| ordering_fields = ['price','id'] |
排序和过滤同时使用
| |
| from rest_framework.filters import SearchFilter, OrderingFilter |
| class BookView(GenericViewSet, ListModelMixin): |
| queryset = Book.objects.all() |
| serializer_class = BookSerializer |
| |
| |
| filter_backends = [SearchFilter,OrderingFilter, ] |
| |
| search_fields=['name',] |
| |
| ordering_fields = ['price'] |
第三方过滤类的使用
| |
| |
| |
| -配置在类属性上:filter_backends = [DjangoFilterBackend] |
| -配置字段:filterset_fields=['name','price'] |
| -http://127.0.0.1:8000/books/?name=红楼梦&price=12 |
分页
自定义分页类
| |
| |
| from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination,CursorPagination |
| |
| class MyPageNumberPagination(PageNumberPagination): |
| |
| page_size = 2 |
| page_query_param = 'page' |
| page_size_query_param = 'size' |
| max_page_size = 5 |
| |
| |
| class MyLimitOffsetPagination(LimitOffsetPagination): |
| |
| default_limit = 2 |
| limit_query_param = 'limit' |
| offset_query_param = 'offset' |
| max_limit = 5 |
| |
| |
| class MyCursorPagination(CursorPagination): |
| cursor_query_param = 'cursor' |
| page_size = 2 |
| ordering = 'id' |
自定义全局异常
| |
| def common_exception_handler(exc, context): |
| |
| response = exception_handler(exc, context) |
| if response: |
| return Response(data={'code': 9998, 'msg': response.data}) |
| else: |
| return Response(data={'code': 9999, 'msg': '服务器异常,请联系系统管理员'}) |
| |
| |
| REST_FRAMEWORK = { |
| 'EXCEPTION_HANDLER': 'app01.exceptions.common_exception_handler', |
| } |
自动生成接口文档
| |
| -前端一批人 |
| -根本不知道你写了什么接口,请求参数什么样,响应数据什么样 |
| -使用什么编码都不知道 |
| -后端一批人 |
| -我们写了很多接口 |
| |
| |
| |
| -1 公司有接口文档平台,后端在平台上录入接口 |
| -2 使用第三方接口文档平台,后端写了在平台录入 |
| -Yapi:开源 |
| -3 使用md,word文档写,写完传到git上 |
| -4 自动生成接口文档(swagger,coreapi) |
| -swagger自动导入,导入到Yapi中 |
| |
| |
| |
| -pip3 install coreapi |
| -在路由中配置 |
| from rest_framework.documentation import include_docs_urls |
| path('docs/', include_docs_urls(title='路飞项目接口文档平台')) |
| -在配置文件中配置 |
| REST_FRAMEWORK = { |
| 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema', |
| } |
| |
| -访问地址http://127.0.0.1:8000/docs(只要路由中有的,都能看到) |
simple-ui
| pip3 install django-simpleui |
| |
| INSTALLED_APPS = [ |
| "simpleui", |
| 'django.contrib.admin', |
| 'django.contrib.auth', |
| 'django.contrib.contenttypes', |
| 'django.contrib.sessions', |
| 'django.contrib.messages', |
| 'django.contrib.staticfiles', |
| 'rest_framework', |
| 'home.apps.HomeConfig', |
| 'corsheaders', |
| 'user.apps.UserConfig' |
| ] |
跨域问题
| |
| pip install django-cors-headers |
| |
| INSTALLED_APPS = ( |
| ... |
| 'corsheaders', |
| ... |
| ) |
| |
| MIDDLEWARE = [ |
| ... |
| 'corsheaders.middleware.CorsMiddleware', |
| 'django.middleware.common.CommonMiddleware', |
| ... |
| ] |
| |
| |
| |
| CORS_ALLOW_CREDENTIALS = True |
| |
| CORS_ORIGIN_ALLOW_ALL = True |
| |
| |
| CORS_ORIGIN_WHITELIST = ( |
| '*' |
| ) |
| CORS_ALLOW_METHODS = ( |
| 'DELETE', |
| 'GET', |
| 'OPTIONS', |
| 'PATCH', |
| 'POST', |
| 'PUT', |
| 'VIEW', |
| ) |
| |
| |
| CORS_ALLOW_HEADERS = ( |
| 'XMLHttpRequest', |
| 'X_FILENAME', |
| 'accept-encoding', |
| 'authorization', |
| 'content-type', |
| 'dnt', |
| 'origin', |
| 'user-agent', |
| 'x-csrftoken', |
| 'x-requested-with', |
| 'Pragma', |
| 'token' |
| ) |
redis连接
| pip install django-redis |
| |
| |
| CACHES = { |
| "default": { |
| "BACKEND": "django_redis.cache.RedisCache", |
| "LOCATION": "redis://127.0.0.1:6379", |
| "OPTIONS": { |
| "CLIENT_CLASS": "django_redis.client.DefaultClient", |
| "CONNECTION_POOL_KWARGS": {"max_connections": 100} |
| |
| } |
| } |
| } |
| |
| |
| |
| from django_redis import get_redis_connection |
| def test(request): |
| conn = get_redis_connection() |
| res=conn.get('name') |
| print(res) |
| return HttpResponse('ok') |
| |
| |
| |
| cache.set('name','xxx') |
| |
| |
日志
| |
| LOGGING = { |
| 'version': 1, |
| 'disable_existing_loggers': False, |
| 'formatters': { |
| 'verbose': { |
| 'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s' |
| }, |
| 'simple': { |
| 'format': '%(levelname)s %(module)s %(lineno)d %(message)s' |
| }, |
| }, |
| 'filters': { |
| 'require_debug_true': { |
| '()': 'django.utils.log.RequireDebugTrue', |
| }, |
| }, |
| 'handlers': { |
| 'console': { |
| 'level': 'DEBUG', |
| 'filters': ['require_debug_true'], |
| 'class': 'logging.StreamHandler', |
| 'formatter': 'simple' |
| }, |
| 'file': { |
| 'level': 'INFO', |
| 'class': 'logging.handlers.RotatingFileHandler', |
| |
| 'filename': os.path.join(os.path.dirname(BASE_DIR), "logs/fuguang.log"), |
| |
| 'maxBytes': 300 * 1024 * 1024, |
| |
| 'backupCount': 10, |
| |
| 'formatter': 'verbose' |
| }, |
| }, |
| |
| 'loggers': { |
| 'django': { |
| 'handlers': ['console', 'file'], |
| 'propagate': True, |
| }, |
| } |
| } |
Response的封装
简化代码
| |
| from rest_framework.response import Response |
| class APIResponse(Response): |
| def __init__(self, status=None, headers=None, **kwargs): |
| data = {'code': 100, 'msg': '成功'} |
| if kwargs: |
| data.update(kwargs) |
| |
| super(APIResponse, self).__init__(data=data, status=status, headers=headers) |
| |
| |
| return APIResponse(username='lqz',token='ssss') |
回顾
| 1 drf入门规范 |
| -前端后开发模式 |
| -API接口 |
| -postman使用 |
| -做接口测试 |
| -restful规范:10条 |
| -djangorestframework |
| -APIVIew的执行流程 |
| -Request对象的源码 |
| |
| 2 序列化 |
| -序列化和反序列化 |
| -instance,many=True,ser.data |
| -data,ser.is_valid--->ser.save(updata,create) |
| -Serializer |
| -写一个个字段 |
| -字段类型 |
| -字段属性 |
| -重写update,create(跟表模型没有直接联系) |
| -局部钩子,全局钩子 |
| |
| -ModelSerializer |
| -重写字段 |
| -SerializerMethodField+配套一个函数get_字段名 |
| -class Meta: |
| 表模型 |
| 序列化和反序列化的字段 |
| -局部和全局钩子 |
| -重写update和create |
| 3 请求与响应 |
| -Request类 |
| -解析的编码:局部和全局配置 |
| |
| -Response类:data,status,header |
| response.data---> |
| -响应的格式:局部和全局配置 |
| 4 视图 |
| -两个视图基类 |
| -APIView |
| -认证类 |
| -权限类 |
| -GenericAPIView |
| -两个类属性 |
| -获取单个 |
| -获取所有 |
| -获取序列化的类 |
| -5个视图扩展类 |
| -5个接口:list,retrieve,destory,update,create |
| -9个视图子类 |
| -写两个类属性 |
| -视图集 |
| -ViewSetMixin:重写了as_view,路由配置变了,自动生成路由 |
| -ViewSet:ViewSetMixin+APIView |
| GenericViewSet:ViewSetMixin+GenericAPIView |
| ModelViewSet:5个视图扩展类+ViewSetMixin+GenericAPIView |
| ReadOnlyModelViewSet: |
| |
| 5 路由 |
| -自动生成路由:继承自ViewSetMixin的视图类 |
| -action装饰器 |
| -在视图类对象中存在:self.action |
| 6 认证,权限,频率 |
| -源码:为什么认证类,配置到视图类中就会执行 |
| -写一个类,继承基类,重写某个方法,全局配置局部配置 |
| |
| 7 过滤,排序(查询所有) |
| -继承了GenericAPIView+ListModelMixin |
| -在视图类属性中配置 filter_backends=[内置,第三方,自己写的] |
| |
| -自定义过滤类,继承:BaseFilterBackend,重写filter_queryset |
| 8 分页 |
| -三种分页方式 |
| -配置在继承了GenericAPIView+ListModelMixin的视图类的pagination_class类属性 |
| |
| -继承APIView,需要自己写 |
| |
| 9 全局异常处理 |
| -写一个函数 |
| -配置文件配置 |
| |
| |
| 10 自动生成接口文档 |
| -接口文档有规范 |
| -yapi:如何使用 |
| |
| 11 jwt |
| 12 simple-ui的使用 |
| 13 跨域问题 |
| |
| http://localhost:3000/api/goods/?pageSzie=3&page=0 |
| |
| http://localhost:3000/api/goods/?pageSize=3&page=0 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具