1. CBV
CBV基于反射实现根据请求方式不同,执行不同的方法
原理: url -> view方法 -> dispatch方法(反射执行其他:GET/POST/DELETE/PUT)
1)简单案例:
views.py:
from django.views import View
class MyBaseView(object):
def dispatch(self, request, *args, **kwargs):
ret = super(StudentView, self).dispatch(request, *args, **kwargs)
return ret
class StudentsView(MyBaseView, View):
# 只展示4个常用的
def get(self, request, *args, **kwargs):
return HttpResponse('GET')
def post(self, request, *args, **kwargs):
return HttpResponse('POST')
def put(self, request, *args, **kwargs):
return HttpResponse('PUT')
def delete(self, request, *args, **kwargs):
return HttpResponse('DELETE')
urls.py:
urlpatterns = [
url(r'^users/', views.UsersView.as_view()),
]
2)CSV解决csrf认证问题
方式一:
from django.views import View
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
class StudentsView(MyBaseView, View):
# 加在单独的方法中无效,只能加载dispatch方法中
@method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
ret = super(StudentView, self).dispatch(request, *args, **kwargs)
return ret
# 只展示4个常用的
def get(self, request, *args, **kwargs):
return HttpResponse('GET')
def post(self, request, *args, **kwargs):
return HttpResponse('POST')
def put(self, request, *args, **kwargs):
return HttpResponse('PUT')
def delete(self, request, *args, **kwargs):
return HttpResponse('DELETE')
方式二
from django.views import View
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
@method_decorator(csrf_exempt, name='dispatch')
class StudentsView(MyBaseView, View):
# 只展示4个常用的
def get(self, request, *args, **kwargs):
return HttpResponse('GET')
def post(self, request, *args, **kwargs):
return HttpResponse('POST')
def put(self, request, *args, **kwargs):
return HttpResponse('PUT')
def delete(self, request, *args, **kwargs):
return HttpResponse('DELETE')
2. restful规范
如果一个架构符合REST的约束条件和原则,即称其为RESTful框架
1) 面向资源编程:每个URL代表一种资源,URL中尽量不要用动词,用名词
2)根据method不同,进行不同的操作:
3)在URL中体现版本:
https://www.example.com/v1/test
https://v1.example.com/test
4)在URL中体现是否是API
https://api.example.com 尽量将API部署在专用域名(存在跨域问题)
https://example.org/api/
5)在URL中的过滤条件
http://api.example.com/v1/zoos?limit=10 指定返回记录的数量
http://api.example.com/v1/zoos?page=2?per_page=100 指定第几页,以及每页的记录数
6)尽量使用HTTPS
7)响应时设置状态码
8)返回值
9)返回错误信息,状态码是4XX时,应返回错误信息,error当作key
{error: "Invalid api key"}
10)Hypermedia API,RESTFUL API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档也知道下一步应该做什么
{"link": {
"rel": "collection https://www.example.com/zoos",
"href": "https://api.example.com/zoos",
"title": "list of zoos",
"type": "application/vnd.yourformat+json"
}}
2. restframework实现序列化与反序列化
ModelSerializer
urls.py
from django.urls import path
from .views import BookView, BookEditView, BookModelViewSet
urlpatterns = [
# path('list', BookView.as_view()),
# path('retrieve/', BookEditView.as_view()),
]
path('list', BookModelViewSet.as_view({"get": "list", "post":"create"})),
path('retrieve/', BookModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
]
serializers.py 序列化组件
from rest_framework import serializers
from .models import Book
class PublisherSerializer(serializers.Serializer):
id = serializers.IntegerField()
title = serializers.CharField(max_length=32)
class AuthorSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField(max_length=32)
def my_validate(value):
if "敏感信息" in value.lower():
raise serializers.ValidationError("不能含有敏感信息")
else:
return value
class BookSerializer(serializers.ModelSerializer):
category_display = serializers.SerializerMethodField(read_only=True)
publisher_info = serializers.SerializerMethodField(read_only=True)
authors = serializers.SerializerMethodField(read_only=True)
def get_category_display(self, obj):
return obj.get_category_display()
def get_authors(self, obj):
authors_query_set = obj.author.all()
return [{"id":author_obj.id, "name":author_obj.name} for author_obj in authors_query_set]
def get_publisher_info(self, obj):
publisher_obj = obj.publisher
return [{"id": publisher_obj.id, "title":publisher_obj.title}]
class Meta:
model = Book
fields = "__all__"
extra_kwargs = {"category": {"write_only": True}, "publisher": {"write_only": True}, "author": {"write_only": True}}
views.py 视图组件
from rest_framework.views import APIView
from rest_framework.viewsets import GenericViewSet
from rest_framework.response import Response
from .serializers import BookSerializer
from models import Book
from rest_framework.viewsets import ViewSetMixin
# 以下为框架提供的:
# from rest_framework import views
# from rest_framework import generics
# from rest_framework import mixins
# from rest_framework import viewsets
class GenericAPIView(APIView):
query_set = None
serializer_class = None
def get_queryset(self):
return self.query_set
def get_serializer(self, *args, **kwargs):
return self.serializer_class(*args, **kwargs)
class ListModelMixin(object):
def list(self, request):
queryset = self.get_queryset()
ret = self.get_serializer(queryset, many=True)
return Response(ret.data)
class CreateModelMixin(object):
def create(self, request):
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return Response(serializer.errors)
class RetrieveModelMixin(object):
def retrieve(self, request, id):
queryset = self.get_queryset().filter(id=id).first()
ret = self.get_serializer(queryset)
return Response(ret.data)
class UpdateModelMixin(object):
def update(self, request, id):
queryset = self.get_queryset().filter(id=id).first()
serializer = self.get_serializer(queryset, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return Response(serializer.errors)
class DestroyModelMixin(object):
def destroy(self, request, id):
queryset = self.get_queryset().filter(id=id).first()
queryset.delete()
return Response("")
class ListCreateAPIView(GenericAPIView, ListModelMixin, CreateModelMixin):
pass
class BookView(ListCreateAPIView):
query_set = Book.objects.all()
serializer_class = BookSerializer
def get(self, request):
return self.list(request)
def post(self, request):
return self.create(request)
class RetrieveUpdateDestroyAPIView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
pass
class BookEditView(RetrieveUpdateDestroyAPIView):
query_set = Book.objects.all()
serializer_class = BookSerializer
def get(self, request, id):
self.retrieve(request, id)
def put(self, request, id):
self.update(request, id)
def delete(self, request, id):
self.destroy(request, id)
class ModelViewSet(ViewSetMixin, GenericAPIView, ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
pass
class BookModelViewSet(ModelViewSet):
query_set = Book.objects.all()
serializer_class = BookSerializer
3.DRF路由组件
from django.urls import path
from .views import BookView, BookEditView, BookModelViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r"", BookModelViewSet)
urlpatterns = [
]
urlpatterns += router.urls
# 如对一个表没有增删改功能,不要使用这种方法暴露过多的url
4. DRF的版本控制组件
实现流程的简单示例:
在Django的settings.py文件中设置与REST_FRAMEWORK相关的配置:
REST_FRAMEWORK = {
"DEFAULT_VERSIONING_CLASS": "utils.version.MyVersion",
# "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.QueryParameterVersioning",
"DEFAULT_VERSION": "v1",
"ALLOWED_VERSIONS": "v1, v2",
"VERSION_PARAM": "version"
}
在Django下创建utils文件夹,并在其下创建version.py文件:
from rest_framework import versioning
class MyVersion(object):
def determine_version(self, request, *args, **kwargs):
# 例:版本号携带在过滤条件中 xxxx?version=v1
version = request.query_params.get("version", "v1")
return version
views.py文件:
from rest_framework.views import APIView
from rest_framework.response import Response
class DemoView(APIView):
def get(self, request):
# 得到版本号,根据版本号的不同返回不同的信息
if request.version == "v1":
return Response("v1版本的数据")
elif request.version == "v2":
return Response("v2版本的数据")
return Response(“不存在的版本”)
5. DRF认证组件
models.py文件:
from django.db import models
class User(models.Model):
username = models.CharField(max_length)
pwd = models.CharField(max_length)
token = models.UUIDField()
type = models.IntegerField(choices=((1, "vip"), (2, "vvip"), (3, "普通")), default=3)
urls.py文件:
from django.urls import path
from .views import LoginView
urlpatterns = [
path(r"login", LoginView.as_view()),
]
views.py文件
from rest_framework.views import APIView
from rest_framework.response import Response
import uuid
from models import User
from utils.auth import MyAuth
class LoginView(APIView):
def post(self, request):
username = request.data.get("username")
pwd = request.data.get("pwd")
token = uuid.uuid4()
User.objects.create(username=username, pwd=pwd, token=token)
return Response("创建用户成功")
class TestView(APIView):
authentication_classes = [MyAuth,]
def get(self, request):
print(request.user)
print(request.auth)
return Response("认证测试")
在utils文件下创建auth.py文件:
from rest_framework.exceptions import AuthenticationFailed
from xxx.models import User
from rest_framework.authentication import BaseAuthentication
class MyAuth(BaseAuthentication):
def authenticate(self, request):
# 真正实现认证的地方
# 做认证,看是否登录
# 从url过滤条件里拿到token
# 去数据库看token是否合法
# 合法的token能够获取用户信息
token = request.query_params.get("token", "")
if not token:
raise AuthenticationFailed("未携带token")
user_obj = User.objects.filter(token=token).first()
if not user_obj:
raise AuthenticationFailed("token不合法")
return (user_obj, token)
6. DRF权限组件
models.py文件:
from django.db import models
class User(models.Model):
username = models.CharField(max_length)
pwd = models.CharField(max_length)
token = models.UUIDField()
type = models.IntegerField(choices=((1, "vip"), (2, "vvip"), (3, "普通")), default=3)
在utils文件夹下创建permission.py文件:
from rest_framework.permissions import BasePermission
class MyPermission(BasePermission):
message = "您没有权限“
def has_permission(self, request, view):
# 判断用户是否有权限
user_obj = request.user
if user_obj.type == 3:
return False
return True
views.py文件:
from rest_framework.views import APIView
from rest_framework.response import Response
import uuid
from models import User
from utils.auth import MyAuth
from utils.permission import MyPermisson
class LoginView(APIView):
def post(self, request):
username = request.data.get("username")
pwd = request.data.get("pwd")
token = uuid.uuid4()
User.objects.create(username=username, pwd=pwd, token=token)
return Response("创建用户成功")
class TestView(APIView):
authentication_classes = [MyAuth,]
permission_classes = [MyPermisson,] # 权限配置
def get(self, request):
print(request.user)
print(request.auth)
return Response("认证测试")
7. DRF频率组件
1)自己实现限流类
utils文件夹下创建throttle.py文件:
from rest_framework.throttling import BaseThrottle
VISIT_RECORD = {}
# 简单实现频率控制
class MyThrottle(BaseThrottle):
def __init__(self):
self.history = None
def allow_request(self, request, view):
# 实现限流的逻辑
# 以IP限流
# 访问列表{IP: [time1, time2, time3]}
# 1. 获取请求的IP地址
ip = request.META.get("REMOTE_ADDR")
# 2. 判断IP地址是否在访问列表:不在,需要给访问列表添加key,value;在,需要把这个IP的访问记录,把当前时间加入到列表
now = time.time()
if ip not in VISIT_RECORD:
VISIT_RECORD[ip] = [now, ]
return True
history = VISIT_RECORD[ip]
history.insert(0, now)
# 3. 确保列表里最新访问时间以及最老的访问时间差为1分钟
while history and history[0] - history[-1] > 60:
history.pop()
self.history = history
# 4. 得到列表长度,判断是否是允许的次数
if len(history) > 3:
return False
else:
return True
def wait(self):
# 返回需要再等多久才能访问
time = 60 - (self.history[0] - self.history[-1])
return time
views.py文件
from rest_framework.views import APIView
from rest_framework.response import Response
import uuid
from models import User
from utils.auth import MyAuth
from utils.permission import MyPermisson
from utils.throttle import MyThrottle
class LoginView(APIView):
def post(self, request):
username = request.data.get("username")
pwd = request.data.get("pwd")
token = uuid.uuid4()
User.objects.create(username=username, pwd=pwd, token=token)
return Response("创建用户成功")
class TestView(APIView):
authentication_classes = [MyAuth,]
permission_classes = [MyPermisson,] # 权限配置
throttle_classes = [MyThrottle,] # 限流配置
def get(self, request):
print(request.user)
print(request.auth)
return Response("认证测试")
2)框架实现的限流类
settings.py文件:
REST_FRAMEWORK = {
"DEFAULT_VERSIONING_CLASS": "utils.version.MyVersion",
# "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.QueryParameterVersioning",
"DEFAULT_VERSION": "v1",
"ALLOWED_VERSIONS": "v1, v2",
"VERSION_PARAM": "version",
"DEFAULT_THROTTLE_RATES": {
"WD": "3/m"
}
}
throttle.py文件:
from rest_framework.throttling import BaseThrottle, SimpleRateThrottle
VISIT_RECORD = {}
# 简单实现频率控制
class MyThrottle(SimpleRateThrottle):
scope = "WD"
def get_cache_key(self, request, view):
key = self.get_ident(request)
return key
8. DRF分页器
1)pageNumber分页
utils文件夹下创建pagination.py文件:
from rest_framework.pagination import PageNumberPagination
class MyPagination(PageNumberPagination):
page_size = 2
page_query_param = "page"
page_size_query_param = "size"
max_page_size = 3
views.py文件:
from rest_framework.views import APIView
from xxx.models import Book
from xxx.serializers import BookSerializer
from utils.pagination import MyPagination
class BookView(APIView):
def get(self, request):
queryset = Book.objects.all()
# 1. 实例化分页器对象
page_obj = MyPagination()
# 2. 调用分页方法去分页queryset
page_queryset = page_obj.paginate_queryset(queryset, request, view=self)
# 3. 把分页好的数据序列化返回
# 4. 带着上一页下一页链接的响应
ser_obj = BookSerializer(page_queryset, many=True)
return page_obj.get_paginated_response(ser_obj.data)
2)limitoffset分页
from rest_framework.pagination import LimitOffsetPagination
class MyPagination(LimitOffsetPagination):
default_limit = 1
limit_query_param = "limit"
offset_query_param = "offset"
max_limit = 3
3)Cursor分页
from rest_framework.pagination import CursorPagination
class MyPagination(CursorPagination):
cursor_query_param = "cursor"
page_size = 2
ordering = "-id"
9. 解析器
解析器的作用就是服务端接受客户端传过来的数据,把数据解析成自己想要的数据类型的过程
1)Django的解析器
Django会把解析好的数据放入request.body中
from django.views import View
from django.http import HttpResponse
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import parsers
class DjangoView(View):
def get(self, request):
print(type(request))
return HttpResponse("dajngo解析器测试")
2)DRF解析器
from django.views import View
from django.http import HttpResponse
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import parsers
class DjangoView(View):
def get(self, request):
print(type(request))
return HttpResponse("django解析器测试")
class DRFView(APIView)
parser_classes = [parsers.JSONParser, ] # 一般不去自己配置
def get(self, request):
print(type(request))
return HttpResponse("DRF解析器测试")
补充
1. django常用中间件
process_request
process_view
process_response
process_exception
process_render_template
2. django的csrf是如何实现的?
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
@csrf_protect
process_view方法
--检查视图是否被csrf_exempt(免除csrf认证)
--去请求体或cookie中获取token
3. 跨域
CORS即Cross Origin Resource Sharing跨域资源共享
1)简单请求和复杂请求:
HTTP方法是下列方法之一:HEAD, GET, POST
HTTP头信息不超过以下几种字段:Accept, Accept-Language, Content-Language, Last-Event-ID
Content-Type只能是下列类型中的一个:application/x-www-form-urlencoded, multipart/form-data, text/plain
任何一个不满足上述要求的请求,即会被认为是复杂请求,复杂请求会先发出一个预请求,也叫预检,OPTIONS请求
2)浏览器的同源策略
跨域是因为浏览器的同源策略导致的,也就是说浏览器会阻止非同源的请求。域名不同、端口不同都属于非同源的。浏览器只阻止表单以及ajax请求,并不会阻止src请求,所以类似cdn, 图片等src请求都可以发
3)解决跨域
(1)JSONP,实现原理是根据浏览器不组织src请求入手来实现的
class Test(APIView):
def get(self, request):
callback = request.query_params.get("callback", "")
ret = callback + "(" + "'success'" + ")"
return HttpResponse(ret)
JSONP解决跨域只能发送get请求,并且实现起来需要前后端交互比较多
(2)添加响应头解决跨域(推荐方法)
在项目下创建middlewares.py文件,并添加在settings.py文件中添加中间件
from django.utils.deprecation import MiddlewreMixin
class MyCors(MiddlewareMixin):
def process_response(self, request, response):
response["Access-Control-Allow-Origin"] = "*"
if request.method == "OPTIONS":
response["Access-Control-Allow-Methods"] = "POST, PUT, DELETE"
response["Access-Control-Allow-Headers"] = "content-type"
return response
4. Django的content-type组件
简单示例,models.py文件:
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey
class Food(models.Model):
title = models.CharField(max_length=32)
class Fruit(models.Model):
title = models.CharField(max_length=32)
class Coupon(models.Model):
title = models.CharField(max_length=32)
content_type = models.ForeignKey(to=ContentType)
object_id = models.IntegerField()
content_obj = GenericForeignKey("content_type", "object_id")
views.py文件:
from rest_framework.views import APIView
from rest_framework.response import Response
from models import Food, Coupon
class DemoView(APIView):
def get(self, request):
food_obj = Food.objects.filter(id=1).first()
# 创建优惠券
# Coupon.objects.create(title="牛奶八折", content_object=food_obj)
# 查询牛奶有哪些优惠券
coupons = food_obj.couponse.all()
# 优惠券差对象
coupon_obj = Coupon.objects.filter(id=1).first()
content_obj = coupon_obj.content_object
# 通过ContentType表找表模型
content = ContentType.objects.filter(app_label="demo", model="food").first()
model_class = content.model_class()
ret = model_class.objects.all()
print(ret)
return Response("ContentType测试")
5. Redis以及python操作Redis
1)Redis是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库
2)Redis有以下特点:
-- Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用
-- Redis支持五种数据类型
-- Redis支持数据库备份
3)Redis的优势:
-- Redis性能极高,读的速度是110000次/s,写的速度是81000次/s
-- Redis丰富的数据类型,String, Lists, Hashes, Sets以及Ordered Sets
-- Redis的所有操作都是原子性的,意思就是要么成功执行,要么完全失败不执行,多个操作支持事务。即MULTI和EXEC指令包起来
-- Redis有丰富的特性,支持publish/subscribe,通知,key过期等等特性
4)Redis配置:
-- 可以通过redis-cli进入交互模式,使用config命令查看或设置配置项。也可以进入配置文件用vim编辑器进行修改
# 获取所有配置项
config get *
# 获取单个配置项
config get loglevel
# 编辑配置
config set loglevel "notice"
5)Redis数据类型
-- String字符串
-- redis的string可以包含任何数据,包括图片以及序列化的对象,一个键最大能存储512mb
-- Hash 哈希
-- redis的hash是一个String类型的key和value的映射表,hash特别适合存储对象,类比python字典
-- List 列表
-- redis的list是简单的字符串列表,按照插入顺序排列,可以从两端进行添加,类似于双向链表,列表还可以进行阻塞
-- Set 集合
-- redis的set是字符串类型的无序且不重复集合。集合是通过哈希表实现的,所以添加、删除、查找的时间复杂度都是O(1)
-- Zset 有序集合
-- redis的zset和set一样,不同的是每个元素都会关联一个double类型的分数,redis正是通过对分数的排序对集合进行有序存储。
6)Python操作redis
import redis
conn = redis.Redis(host="127.0.0.1", port=6379, decode_responses=True)
conn.set("n1", "v1")
conn.hset("n2", "k2", "v2")
conn.hmset("n3", {"k3": "v3", "k4": "v4"})
ret1 = conn.get("n1")
ret2 = conn.hget("n2", "k2")
ret3 = conn.hget("n3", "k3")
ret4 = conn.hget("n3", "k4")
ret5 = conn.hgetall("n3")
print(ret1)
print(ret2)
print(ret3)
print(ret4)
print(ret5)
7)发布者、订阅者模型
pub.py文件:
import redis
conn = redis.Redis(decode_responses=True)
conn.publish("Steven", "Back to the real world")
sub.py文件:
import redis
conn = redis.Redis(decode_responses=True)
# 生成一个订阅者对象
pubsub = conn.pubsub()
# 订阅一个消息
pubsub.subscribe("Steven")
# 创建一个接受
while True:
print("Start to work")
msg = pubsub.parse_response()
print(msg)