前言:当初第一次接触时一脸懵逼,为了赶进度暂时放下了, 现在重新捡起来了, 略有收获, 但是坑太多, 暂时填补了,只等日后时机成熟再填坑。
写在前面:
1) PublishSerializers(queryset, many=True), PublishSerializers(model) serializers可以序列化model和queryset, 序列化queryset时,因为是多个对象, 要加many=True。
2)别忘了在settings中INSTALLED_APPS中注册
3)request.data 拿到的是post和put请求
4)request.GET request._request.GEt 拿的才是get数据
5)还有restframework 中的request 不是以前的request,它变了,request._request才是以前的request
6) Resonse 返回更友好的数据
7)如127.0.0.1:8000/books/?format=json 浏览器访问,可以显示json数据
一 序列化
models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.IntegerField()
pub_date = models.DateField(null=True)
publish = models.ForeignKey("publish", on_delete=models.CASCADE)
authors = models.ManyToManyField("Author")
def __str__(self):
return self.title
class Publish(models.Model):
name = models.CharField(max_length=32)
email = models.EmailField(null=True)
def __str__(self):
return self.name
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
def __str__(self):
return self.name
url.py
from django.contrib import admin
from django.urls import path, re_path
from app001 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('publishes/', views.PublishView.as_view(), name="publish"),
re_path(r'publishes/(?P<pk>\d+)/$', views.PublishDetailView.as_view(), name="detailpublish"),
path('books/', views.BookView.as_view(), name="book"),
re_path(r'books/(\d+)/$', views.BookDetailView.as_view(), name="detailbook")
]
myserlizer.py (知道拼错了, 将错就错, 还有坑太特么多了, mmp,暂时没精力解决了,至以后的自己)
两种序列化方法
from rest_framework import serializers
from app001 import models
# 两种序列化方法, 最好用model的
class PublishSerializers(serializers.Serializer):
name = serializers.CharField(max_length=32)
email = serializers.EmailField()
# 日了狗, 用这个序列化,发get没问题, 发post就说create方法有问题,重写create也不行
class PublishModelSerializers(serializers.ModelSerializer):
class Meta:
model = models.Publish
fields = "__all__"
class BooksSerializers(serializers.Serializer):
title = serializers.CharField(max_length=32)
price = serializers.IntegerField()
pub_date = serializers.DateField()
publish = serializers.CharField(source="publish.name")
# authors = serializers.CharField(source="authors.all")
authors = serializers.SerializerMethodField()
publish = serializers.CharField(source="publish.pk", read_only=True)
# 凉凉, 发post 还是有问题
def get_authors(self, obj):
temp = []
for obj in obj.authors.all():
temp.append(obj.name)
return temp
# ################################################################
class BooksModelSerializers(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = "__all__"
# publish = serializers.HyperlinkedIdentityField(
# view_name="detailpublish",
# lookup_field="publish_id",
# lookup_url_kwarg="pk"
# )
# 自定义字段,显示成一个超链接, url要全部制定name参数
# views还要加参数BooksModelSerializers(book_list, many=True, context={'request': request})
# 同样post凉凉
# 默认是返回一对多,多对多的主键
# 如果想返回名字要自定义, 然后在views重写create
# publish = serializers.CharField(source="publish.name")
# 发put还要YM重写update
# 一对多显示name, 要重写create
# authors = serializers.CharField(source="authors.all")
# "<QuerySet [<Author: えなこ>, <Author: まこち>]>", post凉凉
# 注意下下面有个问题没解决
# 但是下面的重构authors字段 这是显示作者的名字,并不需要重写create, get请求时显示正常, 但是post时会出现问题
# 如果重写authors, validated_data拿不到authors,book.authors.add(*validated_data["authors"])没法绑定关系
# 所以不能和create共存,就是说当post时只能做到让一对一或多对多显示指定名字
# authors = serializers.SerializerMethodField()
#
# def get_authors(self, obj):
# temp = []
# for obj in obj.authors.all():
# temp.append(obj.name)
# return temp
# def create(self, validated_data):
# print("xxxxxx", validated_data)
# book = models.Book.objects.create(title=validated_data["title"], price=validated_data["price"], pub_date=validated_data["pub_date"], publish_id=validated_data["publish"]["name"])
# book.authors.add(*validated_data["authors"])
# return book
views.py
from rest_framework.response import Response
from app001.models import *
from rest_framework.views import APIView
from app001.myserilizer import *
class PublishView(APIView):
def get(self, request):
publish_list = Publish.objects.all()
ps = PublishModelSerializers(publish_list, many=True)
return Response(ps.data)
def post(self, request):
ps = PublishModelSerializers(data=request.data)
if ps.is_valid():
ps.save()
return Response(ps.data)
else:
return Response(ps.errors)
class PublishDetailView(APIView):
def get(self, request, pk):
publish = Publish.objects.filter(pk=pk).first()
ps = PublishModelSerializers(publish)
return Response(ps.data)
def put(self, request, pk):
publish = Publish.objects.filter(pk=pk).first()
ps = PublishModelSerializers(publish, data=request.data)
# 加many=True 不行会报错, 所以面对model还是别好奇加many参数会怎样
if ps.is_valid():
ps.save()
return Response(ps.data)
else:
return Response(ps.errors)
def delete(self, request, pk):
Publish.objects.filter(pk=pk).delete()
return Response()
class BookView(APIView):
def get(self, request):
book_list = Book.objects.all()
bs = BooksModelSerializers(book_list, many=True, context={'request': request})
return Response(bs.data)
def post(self, request):
bs = BooksModelSerializers(data=request.data)
if bs.is_valid():
bs.save()
return Response(bs.data)
else:
return Response(bs.errors)
"""
postman 发post的数据
{
"title": "第四本书",
"price": 123214,
"pub_date": "2011-11-11",
"publish": 1,
"authors": [1, 2]
}
"""
class BookDetailView(APIView):
def get(self, request, id):
book = Book.objects.filter(pk=id).first()
bs = BooksModelSerializers(book, context={'request': request})
return Response(bs.data)
def put(self, request, id):
book = Book.objects.filter(pk=id).first()
bs = BooksModelSerializers(book, data=request.data)
if bs.is_valid():
bs.save()
return Response(bs.data)
else:
return Response(bs.errors)
def delete(self, request, id):
Book.objects.filter(pk=id).delete()
return Response()
二 重写视图的几种方法
1)mixins
views.py
from rest_framework import mixins
from rest_framework import generics
class BookView(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):
queryset = models.Book.objects.all()
serializer_class = BooksModelSerializers
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class BookDetailView(mixins.RetrieveModelMixin, mixins.DestroyModelMixin, mixins.UpdateModelMixin, generics.GenericAPIView):
queryset = models.Book.objects.all()
serializer_class = BooksModelSerializers
def get(self, request, *args, **kwargs):
return self.retrieve(request, id, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
urls.py
path('books/', views.BookView.as_view(), name="book"),
re_path(r'books/(?P<pk>\d+)/$', views.BookDetailView.as_view(), name="detailbook")
2)generics
views.py
from rest_framework import generics
class BookView(generics.ListCreateAPIView):
queryset = Book.objects.all()
serializer_class = BooksModelSerializers
class BookDetailView(generics.RetrieveUpdateDestroyAPIView):
queryset = Book.objects.all()
serializer_class = BooksModelSerializers
urls.py
path('books/', views.BookView.as_view(), name="book"),
re_path(r'books/(?P<pk>\d+)/$', views.BookDetailView.as_view(), name="detailbook")
3)viewsets
views.py
from rest_framework import viewsets
class BookModelView(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BooksModelSerializers
urls.py
path('books/', views.BookModelView.as_view({"get": "list", "post": "create"}), name="book"),
re_path(r'books/(?P<pk>\d+)/$', views.BookModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}),
三 认证
rest_framework的认证逻辑是自己写的,如下面的认证,看看能否在url拿到正确的token信息, 然后返回指定形式的内容
1)继承object, 要写authenticate_header
from rest_framework import exceptions
class TokenAuth(object):
def authenticate(self, request):
token = request.GET.get("token")
token_obj = Token.objects.filter(token=token).first()
if not token_obj:
raise exceptions.AuthenticationField("验证失败")
else:
return token_obj.user.name, token_obj.token
# self.user self,auth
def authenticate_header(self, request):
# 不定义这个函数会报错
pass
2) 继承BaseAuthentication
from rest_framework.authentication import BaseAuthentication
def TokenAuth(BaseAuthtication):
pass
使用的时候:
class Bookview(APIView):
authentication_classes = [TokenAuth,]
# 或者
class BookModelView(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BooksModelSerializers
authentication_classes = TOkenAuth
想让它全局生效时:
在settings中加
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.TokenAuth",],
# 所在位置
}
当设置全局,但是某个部分不需要用它,只需要把它自己的设置成
authentication_classes = []
四 权限
逻辑依然是自己写的, 只是用人家的钩子
class SVIPPERmission(object):
def has_permission(self, request. view):
username = request.user
# 经过认证, 拿到登陆人的request.user
user_type = User.objects.filter(name=username).first().user_type
if user_type == 3:
return True
# 通过权限
else:
return False
使用时:
跟前面的认证一样
class Bookview(APIView):
authentication_classes= [TokenAuth,]
permission_classes = [SVIPPermission,]
# 或者
class BookModelView(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BooksModelSerializers
authentication_classes = TOkenAuth
permission_classes = SVIPPermission
全局:
REST_FRAMEWORK={
"DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
"DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",]
}
五 频率
自己写的频率
在app01.service.throttles.py中:
from rest_framework.throttling import BaseThrottle
VISIT_RECORD={}
class VisitThrottle(BaseThrottle):
def __init__(self):
self.history=None
def allow_request(self,request,view):
remote_addr = request.META.get('REMOTE_ADDR')
print(remote_addr)
import time
ctime=time.time()
if remote_addr not in VISIT_RECORD:
VISIT_RECORD[remote_addr]=[ctime,]
return True
history=VISIT_RECORD.get(remote_addr)
self.history=history
while history and history[-1]<ctime-60:
history.pop()
if len(history)<3:
history.insert(0,ctime)
return True
else:
return False
def wait(self):
import time
ctime=time.time()
return 60-(ctime-self.history[-1])
views中
class BookViewSet(generics.ListCreateAPIView):
throttle_classes = [VisitThrottle,]
queryset = Book.objects.all()
serializer_class = BookSerializers
设置为全局时
REST_FRAMEWORK={
"DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
"DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",],
"DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",]
}
利用自带,人家写好的
在app01.service.throttles.py修改为:
class VisitThrottle(SimpleRateThrottle):
scope="visit_rate"
def get_cache_key(self, request, view):
return self.get_ident(request)
设置
REST_FRAMEWORK={
"DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
"DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",],
"DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",],
"DEFAULT_THROTTLE_RATES":{
"visit_rate":"5/m",
}
}
五 解析器
感觉没什么用,默认自带三个常用解析器
from rest_framework.parsers import JSONParser, FormParser, MultiPartParser, FileUploadParser
class xxview(APIView):
parser_classes = [JSONParser, ]
六 路由
针对的是.ModelViewSet视图,它的url写起来太麻烦
from rest_framework import viewsets
class BookModelView(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BooksModelSerializers
"""
path('books/', views.BookModelView.as_view({"get": "list", "post": "create"}), name="book"),
re_path(r'books/(?P<pk>\d+)/$', views.BookModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}), name="detailbook")
"""
蓑衣用到路由精简代码,在ulrs.py中
from django.contrib import admin
from django.urls import path, re_path, include
from app001 import views
from rest_framework import routers
routers = routers.DefaultRouter()
routers.register('book', views.BookModelView)
# 'book'是访问路径, 可以注册多个
rlpatterns = [
# path('books/', views.BookModelView.as_view({"get": "list", "post": "create"}), name="book"),
# re_path(r'books/(?P<pk>\d+)/$', views.BookModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}), name="detailbook")
path('', include(routers.urls))
# 只要这么写
]
可以正常访问页面
http://127.0.0.1:8000/book/,http://127.0.0.1:8000/book/1, http://127.0.0.1:8000/book/1.json
http://127.0.0.1:8000/book/1/?format=json json格式,
七 分页
from rest_framework.pagination import PageNumberPagination
class BookView(APIView):
def get(self, request):
book_list = Book.objects.all()
pnp = PageNumberPagination()
books_page = pnp.Paginate_queryset(book_list, request, self)
bs = BookModelSerializers(books_page, many=True, context={'request': request})
return Response(bs.data)
在settings中
全局
REST_FRAMEWORK = {
"PAGE_SIZE": 1
}
# 访问 url/?page=1
也可以自定制
from rest_framework.pagination import PageNumberPagination
class MyPageNumberPagination(PageNumberPagination):
page_size = 2 # 每个页面几条数据
page_query_param = 'page' # /?page=1
page_size_query_param = 'size' # 加了这个参数可以访问 /?page=2&size=x, 强制显示x页
max_page_size = 3 # 规定上面的x不能大于3
对于封装完全的modelViewset
from rest_framework import viewsets
class BookModelView(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BooksModelSerializers
pagination_class = MyPageNumberPagination
八 响应器
from rest_framework.response import Response
九 解析器
如下
十 版本控制
views.py
from rest_framework import viewsets
from rest_framework.renderers import JSONRenderer, BrowsableAPIRenderer
from rest_framework.versioning import QueryParameterVersioning, URLPathVersioning
class BookModelView(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BooksModelSerializers
renderer_classes = [JSONRenderer, BrowsableAPIRenderer]
# 局部定义的解析器BrowsableAPIRenderer让json数据变更好看, 默认就是这几个, 根本无需修改
versioning_class = QueryParameterVersioning
# 版本 类似 http://127.0.0.1:8000/book/?version=v1
# versioning_class = URLPathVersioning
# 这个的url形如: http://127.0.0.1:8000/v1/book/ 还得自己配置url, 不知道怎么和router连用
settings.py
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': ['rest_framework.renderers.JSONRenderer', 'rest_framework.renderers.BrowsableAPIRenderer'],
# 全局设置渲染器
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.QueryParameterVersioning',
# 全局版本控制,
'ALLOWED_VERSIONS': ['v1', 'v2'],
# 允许访问的版本
'VERSION_PARAM': 'version',
# 版本的参数, 默认就是version
'DEFAULT_VERSION': 'v1'
# 默认版本, 不加参数, 默认
}
特别的当使用versioning_class = URLPathVersioning, 要配置url
urlpatterns = [
re_path(r'^(?P<version>\w+)/book/$', xxxxxx)
可以在后端拿到版本信息
print(request.version)