复习
"""
1、接口:url+请求参数+响应参数
Postman发送接口请求的工具
method: GET
url: https://api.map.baidu.com/place/v2/search
params:
ak: 6E823f587c95f0148c19993539b99295
region: 上海
query: 肯德基
output: json
2、restful接口规范
https://api.baidu.com/v1/books?ordering=-price&limit=3
get|post|put|patch|delete
{
'status': 0,
'msg': 'ok',
'results': [
{
'title': '西游记',
'img': 'https://api.baidu.com/media/xyj.png'
}
]
}
3、基于规范下原生django接口
api/books/ views.Book.as_view()
api/books/(?P<pk>.*)/ views.Book.as_view()
class Book(View):
def get(self, request, *args, **kwargs):
pk = kwagrs.get('pk')
if pk:
# 单查
else:
# 群查
def post(self, request, *args, **kwargs):
# 增
def delete(self, request, *args, **kwargs):
# 删
def put(self, request, *args, **kwargs):
# 整体改
def patch(self, request, *args, **kwargs):
# 局部改
4、drf
请求的生命周期
APIView(as_view)禁用csrf => View(as_view)=> APIView(dispatch) => 视图类的请求方法 => 响应
请求模块
request完全兼容_request
重点:data/query_params
渲染模块
response数据json与browser两种渲染方式
重点:全局配置/局部配置
self.render_classes
自定义视图类(局部) => APIView视图类 => 自定义drf配置(全局) => drf默认配置
"""
解析模块
为什么要配置解析模块
"""
1)drf给我们通过了多种解析数据包方式的解析类
2)我们可以通过配置来控制前台提交的哪些格式的数据后台在解析,哪些数据不解析
3)全局配置就是针对每一个视图类,局部配置就是针对指定的视图来,让它们可以按照配置规则选择性解析数据
"""
源码入口
request = self.initialize_request(request, *args, **kwargs)
parsers=self.get_parsers(),
return [parser() for parser in self.parser_classes]
全局配置:项目settings.py文件
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
],
}
局部配置:应用views.py的具体视图类
from rest_framework.parsers import JSONParser
class Book(APIView):
parser_classes = [JSONParser]
pass
异常模块
为什么要自定义异常模块
"""
1)所有经过drf的APIView视图类产生的异常,都可以提供异常处理方案
2)drf默认提供了异常处理方案(rest_framework.views.exception_handler),但是处理范围有限
3)drf提供的处理方案两种,处理了返回异常现象,没处理返回None(后续就是服务器抛异常给前台)
4)自定义异常的目的就是解决drf没有处理的异常,让前台得到合理的异常信息返回,后台记录异常具体信息
"""
源码分析
response = self.handle_exception(exc)
exception_handler = self.get_exception_handler()
response = exception_handler(exc, context)
如何使用:自定义exception_handler函数如何书写实现体
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'api.exception.exception_handler',
}
from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework.views import Response
from rest_framework import status
def exception_handler(exc, context):
response = drf_exception_handler(exc, context)
if response is None:
print('%s - %s - %s' % (context['view'], context['request'].method, exc))
return Response({
'detail': '服务器错误'
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR, exception=True)
return response
响应模块
响应类构造器:rest_framework.response.Response
def __init__(self, data=None, status=None,
template_name=None, headers=None,
exception=False, content_type=None):
"""
:param data: 响应数据
:param status: http响应状态码
:param template_name: drf也可以渲染页面,渲染的页面模板地址(不用了解)
:param headers: 响应头
:param exception: 是否异常了
:param content_type: 响应的数据格式(一般不用处理,响应头中带了,且默认是json)
"""
pass
使用:常规实例化响应对象
from rest_framework import status就是解释一堆 数字 网络状态码的模块
return Response(data={数据}, status=status.HTTP_200_OK, headers={设置的响应头})
序列化组件:
知识点:Serializer(偏底层)、ModelSerializer(重点)、ListModelSerializer(辅助群改)
Serializer
序列化准备:
class User(models.Model):
SEX_CHOICES = [
[0, '男'],
[1, '女'],
]
name = models.CharField(max_length=64)
pwd = models.CharField(max_length=32)
phone = models.CharField(max_length=11, null=True, default=None)
sex = models.IntegerField(choices=SEX_CHOICES, default=0)
icon = models.ImageField(upload_to='icon', default='icon/default.jpg')
class Meta:
db_table = 'old_boy_user'
verbose_name = '用户'
verbose_name_plural = verbose_name
def __str__(self):
return '%s' % self.name
from django.contrib import admin
from . import models
admin.site.register(models.User)
INSTALLED_APPS = [
'rest_framework',
]
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'day70',
'USER': 'root',
'PASSWORD': '123'
}
}
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False
urlpatterns = [
url(r'^api/', include('api.urls')),
url(r'^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}),
]
urlpatterns = [
url(r'^users/$', views.User.as_view()),
url(r'^users/(?P<pk>.*)/$', views.User.as_view()),
]
序列化使用
"""
1)设置需要返回给前台 那些model类有对应的 字段,不需要返回的就不用设置了
2)设置方法字段,字段名可以随意,字段值有 get_字段名 提供,来完成一些需要处理在返回的数据
"""
from rest_framework import serializers, exceptions
from django.conf import settings
from . import models
class UserSerializer(serializers.Serializer):
name = serializers.CharField()
phone = serializers.CharField()
gender = serializers.SerializerMethodField()
def get_gender(self, obj):
return obj.get_sex_display()
icon = serializers.SerializerMethodField()
def get_icon(self, obj):
return '%s%s%s' % (r'http://127.0.0.1:8000', settings.MEDIA_URL, str(obj.icon))
"""
1)从数据库中将要序列化给前台的model对象,或是对个model对象查询出来
user_obj = models.User.objects.get(pk=pk) 或者
user_obj_list = models.User.objects.all()
2)将对象交给序列化处理,产生序列化对象,如果序列化的是多个数据,要设置many=True
user_ser = serializers.UserSerializer(user_obj) 或者
user_ser = serializers.UserSerializer(user_obj_list, many=True)
3)序列化 对象.data 就是可以返回给前台的序列化数据
return Response({
'status': 0,
'msg': 0,
'results': user_ser.data
})
"""
class User(APIView):
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
try:
user_obj = models.User.objects.get(pk=pk)
user_ser = serializers.UserSerializer(user_obj)
return Response({
'status': 0,
'msg': 0,
'results': user_ser.data
})
except:
return Response({
'status': 2,
'msg': '用户不存在',
})
else:
user_obj_list = models.User.objects.all()
user_ser_data = serializers.UserSerializer(user_obj_list, many=True).data
return Response({
'status': 0,
'msg': 0,
'results': user_ser_data
})
反序列化使用
"""
1)设置必填与选填序列化字段,设置校验规则
2)为需要额外校验的字段提供局部钩子函数,如果该字段不入库,且不参与全局钩子校验,可以将值取出校验
3)为有联合关系的字段们提供全局钩子函数,如果某些字段不入库,可以将值取出校验
4)重写create方法,完成校验通过的数据入库工作,得到新增的对象
"""
class UserDeserializer(serializers.Serializer):
name = serializers.CharField(
max_length=64,
min_length=3,
error_messages={
'max_length': '太长',
'min_length': '太短'
}
)
pwd = serializers.CharField()
phone = serializers.CharField(required=False)
sex = serializers.IntegerField(required=False)
re_pwd = serializers.CharField(required=True)
def validate_name(self, value):
if 'g' in value.lower():
raise exceptions.ValidationError('名字非法,是个鸡贼!')
return value
def validate(self, attrs):
pwd = attrs.get('pwd')
re_pwd = attrs.pop('re_pwd')
if pwd != re_pwd:
raise exceptions.ValidationError({'pwd&re_pwd': '两次密码不一致'})
return attrs
def create(self, validated_data):
return models.User.objects.create(**validated_data)
"""
1)book_ser = serializers.UserDeserializer(data=request_data) # 数据必须赋值data
2)book_ser.is_valid() # 结果为 通过 | 不通过
3)不通过返回 book_ser.errors 给前台,通过 book_ser.save() 得到新增的对象,再正常返回
"""
class User(APIView):
def post(self, request, *args, **kwargs):
request_data = request.data
if not isinstance(request_data, dict) or request_data == {}:
return Response({
'status': 1,
'msg': '数据有误',
})
book_ser = serializers.UserDeserializer(data=request_data)
if book_ser.is_valid():
book_obj = book_ser.save()
return Response({
'status': 0,
'msg': 'ok',
'results': serializers.UserSerializer(book_obj).data
})
else:
return Response({
'status': 1,
'msg': book_ser.errors,
})