阅读:77
api部署在专有域名下
路径不能有动词
版本在地址后拼接,或在header中
不同请求不同方式
通过参数过滤
状态码, 200,201,204,401,403,404,500
使用json返回数据
pip install djangorestframework
https://q1mi.github.io/Django-REST-framework-documentation/
INSTALLED_APPS = [
'rest_framework',
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': ( # 身份验证
'rest_framework.authentication.BasicAuthentication', # 需要登录,用于测试,弹出框用于登录
'rest_framework.authentication.SessionAuthentication', # session登录
),
'DEFAULT_PERMISSION_CLASSES': ( # 权限验证(全局)
'rest_framework.permissions.IsAuthenticated', # 普通用户
'rest_framework.permissions.AllowAny', # 所有用户
'rest_framework.permissions.IsAdminUser', # 管理员
),
'DEFAULT_THROTTLE_CLASSES': ( # 限流用户
'rest_framework.throttling.AnonRateThrottle', # 限制未授权用户
'rest_framework.throttling.UserRateThrottle' # 限制认证用户
),
'DEFAULT_THROTTLE_RATES': { # 限流量
'anon': '100/day', # 未授权用户访问限制次数, 1/minute
'user': '1000/day', # 认证用户访问限制次数
'my_throttle': '10/minute', # 自定义限制, 通用视图中用throttle_scope添加限制
},
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', # 默认分页器
'PAGE_SIZE': 100, # 默认每页数量,使用全局的时候无法用过参数修改每页数量
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
}
from rest_framework.routers import DefaultRouter,SimpleRouter
urlpatterns = [
path('api/', include('rest_framework.urls'))
]
job_router = SimpleRouter() # 创建router
job_router.register(prefix='manager/job', viewset=ModelViewSetApi, basename='job') # 注册view视图, prefix基本路径, viewset注册的视图, basename url的name的基本部分' basename+urlname
# 访问时根据 manager/job/-> list post , manager/job/pk put delete get, 默认没有自定义方法
# 还可通过加 .json 返回json格式的数据
urlpatterns += job_router.urls # 添加路由
print(urlpatterns)
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render
from .models import Jobs
from rest_framework import viewsets
from django.views.generic import ListView
from .serializer import JobSerializer, LabelSerializer # 从序列化器文件引入序列化器
class Index(ListView): # 通用视图,具体看django笔记
template_name = 'job/index.html'
paginate_by = 10
job_serializer = JobSerializer(instance=Jobs.objects.all(), many=True) # 序列化
model = Jobs
# def get_queryset(self):
# return Jobs.objects.all()
# def get(self, request, *args, **kwargs): # 重写get参数
#
# print(self.job_serializer)
# return JsonResponse(self.job_serializer.data, safe=False)
def get_context_data(self, *, object_list=None, **kwargs):
context = super().get_context_data()
context['another'] = '其他的参数'
context['page_range'] = pages_divider(context['page_obj'].number, context['paginator'].page_range)
# print(context)
"""
{'paginator': None, 'page_obj': None, 'is_paginated': False, 'object_list': <QuerySet [<Jobs: Jobs object (00018e71bca90ceacb1bacde563bd236)>]>, 'jobs_list': <QuerySet [<Jobs: Jobs object (00018e71bca90ceacb1bacde563bd236)>]>, 'view': <job.views.Index object at 0x0000022DCD9883D0>, 'another': '其他的参数'}
"""
return context
def post(self, request, *args, **kwargs):
data = request.POST.get('data')
print(data)
d = JobSerializer(data=data) # 反序列化, 创建数据的序列化器
if d.is_valid(raise_exception=True): # 数据格式检验
print(d)
d.save() # 保存数据
return HttpResponse(data)
def pages_divider(page, page_range, perpage=5): # 获取页码范围
page = int(page)
if len(page_range) < perpage:
return page_range
if perpage / 2 - int(perpage / 2) == 0.5:
start = page - int(perpage / 2)
end = page + int(perpage / 2)
if start < 1:
start = 1
end = perpage
if end > page_range[-1]:
end = page_range[-1]
start = end - perpage
return range(start, end + 1)
else:
start = page - int(perpage / 2) + 1
end = page + int(perpage / 2)
if start < 1:
start = 1
end = perpage
if end > page_range[-1]:
end = page_range[-1]
start = end - perpage + 1
return range(start, end + 1)
from rest_framework.views import APIView # 基本视图
from rest_framework.response import Response # DRF响应
from rest_framework import status # DRF状态码
class IndexApi(APIView): # 使用DRF的类视图
def get(self, request): # get方法接口
d = request.query_params # 获取get参数
page = request.query_params['page']
return Response(data={'d': 1}, status=status.HTTP_200_OK) # 使用DRF的响应,及状态码
def post(self, request): # post方法接口
d = request.data # 获取表单信息
return Response({'d': 1})
class DetailApi(APIView):
def get(self, request):
id_job = request.query_params['id']
job = Jobs.objects.get(id=id_job) # 获取要展示model
s_job = JobModelSerializer(instance=job, many=False)
return Response(data=s_job.data, status=status.HTTP_200_OK)
def post(self, request):
data = request.data
s = JobModelSerializer(data=data) # 用表单数据创建序列化实例
if s.is_valid(raise_exception=True): # 验证数据格式
s.save() # 验证通过保存数据
return Response(data)
raise serializers.ValidationError('error') # 不通过报错
def put(self, request): # put方法接口
data = request.data
job = Jobs.objects.get(id=data['id']) # 获取要更新的model
s = JobModelSerializer(instance=job, data=data) # 更新数据的序列化器
if s.is_valid(raise_exception=True):
s.save()
return Response(data)
raise serializers.ValidationError('error')
def delete(self, request): # delete方法接口
job = Jobs.objects.get(id=request.query_params['id']).delete()
return Response(1, status=200)
三个属性三个方法 queryset,serializer_class,lookup_field及pagination_class(分页器)
from rest_framework.generics import GenericAPIView # Generic视图
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin
from rest_framework.response import Response
from rest_framework import status
class GenericListApi(GenericAPIView): # 二级视图-列表
template_name = 'job/index.html'
queryset = Jobs.objects.all() # 通用数据集,必须用queryset
serializer_class = JobModelSerializer # 通用序列化器,必须用serializer_class
class Pager(PageNumberPagination): # 创建自定义DRF分页器, 也可以在settings直接写全局分页配置, 继承GenericAPIView的类才能使用
max_page_size = 10 # 每页最大显示数
page_size = 5 # 默认每页数量
page_query_param = 'page' # 页码参数名
page_size_query_param = 'page_size'
pagination_class = Pager
def get(self, request, page):
queryset= self.get_queryset() # 获取数据用self.get_queryset()
paginate_queryset = self.paginate_queryset(self.filter_queryset(queryset)) # 获取分页后的数据
serializer = self.get_serializer(instance=job, many=True) # 获取序列化器用, self.get_serializer(instance=job,
# many=True)
return Response(data=serializer.data, status=status.HTTP_200_OK)
return render(request, self.template_name, {'data': paginate_queryset}) 3 也可以使用django的响应
def post(self,request):
serializer = self.get_serizlizer(data=request.data, many=True)
if serizlizer.is_valued():
serizlizer.save()
return Response(1)
raise serializers.ValidationError('error')
class GenericDetailApi(GenericAPIView): # 二级视图-详情
queryset = Jobs.objects.all() # 一般都是固定的 模型的all, 想要展示的数据的所有
serializer_class = JobModelSerializer # 数据对应的序列化器
lookup_field = 'pk' # 主键参数名(默认pk), 使用get_object方法使用的get参数
# lookup_url_kwarg =
def get(self, request, pk):
job = self.get_object() # 通过lookup_field, 从queryset中get想要的数据
serializer = self.get_serializer(instance=job) # 获取序列化器
return Response(data=serializer.data, status=status.HTTP_200_OK)
def put(self, request, pk):
job = self.get_object() # 会自动使用lookup_field及传入的值get对应model
serializer = self.get_serializer(instance=job, data=request.data)
return Response(data=serializer.data, status=status.HTTP_200_OK)
def delete(self, pk):
job = self.get_object()
job.delete()
return Response(data='success', status=status.HTTP_203_NON_AUTHORITATIVE_INFORMATION)
from rest_framework.generics import GenericAPIView # Generic视图
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin
from rest_framework.response import Response
from rest_framework import status
class MixinListApi(GenericAPIView, ListModelMixin, CreateModelMixin): # Mixin, 通用的增删改查
queryset = Jobs.objects.all()[:200]
serializer_class = JobModelSerializer
paginate_by = 20 # 分页数量
page_kwarg = 'page' # 页码参数
def get(self, request): # 获取
return self.list(request) # 这里的方法就是继承的ListModelMixin 创建的方法, 获取数据
def post(self, request): # 创建
return self.create(request) # CreateModelMixin的方法, 创建model
class MixinDetailApi(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin): #
queryset = Jobs.objects.all()
serializer_class = JobModelSerializer
lookup_field = 'pk' # 主键参数名(默认pk)
lookup_url_kwarg = 'pk'
def get(self, request, pk):
return self.retrieve(request)
def put(self, request, pk): # 修改
return self.update(request) # 修改pk对应model
def delete(self, request, pk): # 删除
return self.delete(request) # DestroyModelMixin的方法, 删除pk对应model
views.ModelViewSetApi.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
from rest_framework.viewsets import ViewSet, ModelViewSet, ReadOnlyModelViewSet
from django.shortcuts import get_object_or_404
# 视图集>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>.
"""
ViewSet 路由映射 , path('api', viewset.as_view({'get':'list','post':'create'}),)
urlpatterns = [
path('ModelViewSetApi/', views.ModelViewSetApi.as_view({'get': 'list', 'post': 'create'})),
path('ModelViewSetApi/<str:pk>/', views.ModelViewSetApi.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
]
GenericViewSet 路由映射 三个属性三个方法 queryset,serializer_class,lookup_field, get_queryset, get_serizlizer, get_object
ModelViewSet 增删改查,三个属性三个方法
ReadOnlyModelViewSet # 获取单个,获取列表,三个属性三个方法
"""
# 视图集原理
class ViewSetApi(ViewSet): # 提供了通过as_view映射请求方式到本身的方法(可以映射自定义方法),viewset.as_view({'get':'list','post':'create'}
paginate_by = 10
page_kwarg = 'page' # 页码参数
def list(self, request):
queryset = Jobs.objects.all()
p = Paginator(queryset, 10)
serializer = JobModelSerializer(instance=p.page(request.query_params['page']), many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
def retrieve(self, request, pk):
queryset = Jobs.objects.all()
job = get_object_or_404(queryset, pk=pk)
serializer = JobModelSerializer(instance=job)
return Response(serializer.data, status=status.HTTP_200_OK)
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination
# 分页器
class Pager(PageNumberPagination): # 创建自定义DRF分页器, 也可以在settings直接写全局分页配置, 继承GenericAPIView的类才能使用
max_page_size = 10 # 每页最大显示数
page_query_param = 'page' # 页码参数名
page_size_query_param = 'page_size' # 页码数据量参数名
# 只读视图集
class ReadOnlyModelViewSetApi(ReadOnlyModelViewSet): # 读取单个和列表,及请求映射
queryset = Jobs.objects.all()
serializer_class = JobModelSerializer
lookup_field = 'pk'
pagination_class = Pager # 使用自定义分页器,请求时必须写page(page_query_param)和page_size(page_size_query_param)这两个参数, 继承GenericAPIView的类才能使用
from rest_framework.decorators import action
# 通用视图集
class ModelViewSetApi(ModelViewSet): # 通用增删改查及请求方式映射
queryset = Jobs.objects.all()
serializer_class = JobModelSerializer
lookup_field = 'pk'
pagination_class = Pager # 使用自定义分页器,请求时必须写page(page_query_param)和page_size(page_size_query_param)这两个参数, 继承GenericAPIView的类才能使用
permission_class = [rest_framework.permissions.AllowAny] # 局部权限验证
authentication_classes = [IsAuthenticated] # 局部身份验证
throttle_classes = [AnonRateThrottle, UserRateThrottle] # 局部限流
throttle_scope = "my_throttle" # 可选限流
# 使DRF自动创建路由, methods允许访问的方式, url_path路径, basename(router中)+url_name = name(path中), detail是否需要传入lookup_field
@action(methods=['put'], detail=True, url_path='change', url_name='change')
def update_job_requires(self,request, pk): # 局部更新数据 prefix/<str:pk>/url_path
job = self.get_object()
serializer = self.get_serializer(instance=job, data=request.data, partial=True) # partial修改部分数据
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(request.data)
from rest_framework.routers import DefaultRouter,SimpleRouter
urlpatterns = []
job_router = DefaultRouter() # 创建router
job_router.register(prefix='job', viewset=ModelViewSetApi, basename='job') # 注册view视图, prefix基本路径, viewset注册的视图, basename url的name的基本部分' basename+urlname
# 访问时根据 manager/job/-> list post , manager/job/pk put delete get, 默认不创建自定义方法的路由, 需要加action装饰器
# 还可通过加 .json 返回json格式的数据
urlpatterns += job_router.urls # 添加路由
视图集自动生成的url (DefaultRouter)
<URLPattern '^job/$' [name='jobs-list']>,
<URLPattern '^job\.(?P<format>[a-z0-9]+)/?$' [name='jobs-list']>,
<URLPattern '^job/(?P<pk>[^/.]+)/$' [name='jobs-detail']>,
<URLPattern '^job/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$' [name='jobs-detail']>,
<URLPattern '^job/(?P<pk>[^/.]+)/change/$' [name='jobs-c']>,
<URLPattern '^job/(?P<pk>[^/.]+)/change\.(?P<format>[a-z0-9]+)/?$' [name='jobs-c']>,
<URLPattern '^$' [name='api-root']>,
<URLPattern '^\.(?P<format>[a-z0-9]+)/?$' [name='api-root']>]
视图集自动生成的url (SimpleRouter)
<URLPattern '^job/$' [name='jobs-list']>,
<URLPattern '^job/(?P<pk>[^/.]+)/$' [name='jobs-detail']>,
<URLPattern '^job/(?P<pk>[^/.]+)/change/$' [name='jobs-change']>] # 自定义方法的路由
from rest_framework import serializers
import datetime
from .models import Jobs
class JobSerializer(serializers.Serializer): # 创建序列化类
def create(self, validated_data): # 保存数据
Jobs.objects.create(**validated_data)
return 1
def update(self, instance, validated_data): # 更新数据
for k, v in validated_data.items():
if hasattr(instance, k): # 判断是否有属性
setattr(instance, k, v) # 设置属性值
instance.save()
return 1
id = serializers.PrimaryKeyRelatedField(read_only=True) # 选择想要展示的字段,字段名,字段类型和model的字段一样, 主键`外键
name = serializers.CharField(max_length=128)
company = serializers.CharField(max_length=200)
salary = serializers.CharField(max_length=64)
requires = serializers.CharField()
issue = serializers.DateTimeField()
education = serializers.CharField(max_length=64, allow_null=True, allow_blank=True)
position = serializers.CharField(max_length=128, allow_null=True)
platform = serializers.CharField(max_length=20)
get_data = serializers.DateTimeField(default=datetime.datetime.now())
# label_set = serializers.PrimaryKeyRelatedField(read_only=True, many=True) # 关联他的的对象, 名字就是related_name的值
label_set = serializers.StringRelatedField(read_only=True,
many=True) # 关联他的的对象的__str__(要有__str__方法), 名字就是related_name的值
@staticmethod # 用不着self或cls的话可以用静态方法
def validate_name(value): # 单字段校验
if '?' in value:
raise serializers.ValidationError("不能包含特殊符号")
return value
@staticmethod
def validate(*args): # 多字段校验, 直接用一个参数就行, 不用加*和索引访问
# print(args[0])
# print(args)
if args[0]['issue'] > args[0]['get_data']:
raise serializers.ValidationError("发布时间大于爬取时间")
return args[0]
# def validate(data): # 多字段校验, 直接用一个参数就行, 不用加*和索引访问
# if data['issue'] > data['get_data']:
# raise serializers.ValidationError("发布时间大于爬取时间")
# return data
class LabelSerializer(serializers.Serializer):
def create(self, validated_data):
pass
def update(self, instance, validated_data):
pass
job = serializers.PrimaryKeyRelatedField(read_only=True) # 显示关联对象的id
# job = serializers.StringRelatedField(read_only=True) # 显示关联对象的__str__的信息
# job = JobSerializer() # 显示关联对象的序列化器所有字段的信息
value = serializers.CharField(max_length=64)
class JobModelSerializer(serializers.ModelSerializer): # 模型序列化器, 自动生成对应的序列化字段
"""
内置了create,update方法
"""
token = serializers.CharField(max_length=100, write_only=True) # 添加额外的字段, write_only只反序列化不显示(序列化)
class Meta:
model = Jobs # 自动生成所有序列化字段
fields = "__all__" # all生成所有字段, ['name','issue','salary'] 列表或元组,选择部分字段
read_only_fields = ['id'] # 设置只读字段, 可以显示, 修改无效
extra_kwargs = {
'name': { # 修改字段参数, 默认的字段参数不满足时可以在这额外添加或修改
'max_length': 200
},
'position': {
'max_length': 50
}
}
"""
字段选项
LIST_SERIALIZER_KWARGS = (
'read_only', 'write_only', 'required', 'default', 'initial', 'source',
'label', 'help_text', 'style', 'error_messages', 'allow_empty',
'instance', 'data', 'partial', 'context', 'allow_null',
'max_length', 'min_length'
)
read_only 设置只读字段, 修改无效
write_only
反序列化时, 设置的default read_only=True required=False 不需要传参
字段类型(序列化器对应的model字段类型)
serializer_field_mapping = {
models.AutoField: IntegerField,
models.BigIntegerField: IntegerField,
models.BooleanField: BooleanField,
models.CharField: CharField,
models.CommaSeparatedIntegerField: CharField,
models.DateField: DateField,
models.DateTimeField: DateTimeField,
models.DecimalField: DecimalField,
models.DurationField: DurationField,
models.EmailField: EmailField,
models.Field: ModelField,
models.FileField: FileField,
models.FloatField: FloatField,
models.ImageField: ImageField,
models.IntegerField: IntegerField,
models.NullBooleanField: BooleanField,
models.PositiveIntegerField: IntegerField,
models.PositiveSmallIntegerField: IntegerField,
models.SlugField: SlugField,
models.SmallIntegerField: IntegerField,
models.TextField: CharField,
models.TimeField: TimeField,
models.URLField: URLField,
models.UUIDField: UUIDField,
models.GenericIPAddressField: IPAddressField,
models.FilePathField: FilePathField,
}
"""