Django项目总结: REST Framework 用户注册登录,权限控制,级联操作查询,节流控制自定义
用户注册登录,权限控制,级联操作查询,节流控制自定义
需求
- 存在级联数据
- 用户和收货地址
- 节流
分析
- 数据开始
- 模型定义
- 用户和收货地址 一对多
- 用户表
- 地址表
- ForeignKey
- 序列化
- 级联数据如何实现序列化???
- 节流
节流器
- BaseThrottle
- allow_request
- 是否允许的请求的核心
- get_ident
- 获取客户端唯一标识
- wait
- allow_request
- SimpleRateThrottle
- get_cache_key
- 获取缓存标识
- get_rate
- 获取频率
- parse_rate
- 转换频率
- num/duration
- duration
- s
- m
- h
- d
- duration
- num/duration
- 转换频率
- allow_request
- 是否允许请求
- 重写的方法
- throttle_success
- 允许请求,进行请求记录
- throttle_failure
- 不允许请求
- wait
- 还有多少时间之后允许
- get_cache_key
- AnonRateThrottle
- get_cache_key
- 获取缓存key的原则
- get_cache_key
- UserRateThrottle
- 和上面一模一样
- ScopedRateThrottle
- 和上面一样
- 多写了从属性中获取频率
技能点
- HTTP_X_FORWARDED_FOR
- 获取你的原始IP
- 通过的普通的代理发送请求的请求
- 如果获取REMOTE_ADDR获取到的是代理IP
- 获取你的原始IP
- 代理
- 普通代理
- 高匿代理
- 效率越低,请求速度越慢
model.py
from django.db import models class UserModel(models.Model): u_name = models.CharField(max_length=16, unique=True) u_password = models.CharField(max_length=256) class Address(models.Model): a_address = models.CharField(max_length=128) a_user = models.ForeignKey(UserModel, related_name='address_list', on_delete=models.SET_NULL, null=True, blank=True)
throttles.py
from rest_framework.throttling import SimpleRateThrottle from App.models import UserModel # 节流模块 class UserThrottle(SimpleRateThrottle): # user 和 address 对应settings中的 DEFAULT_THROTTLE_RATES 参数 scope = 'user' def get_cache_key(self, request, view): if isinstance(request.user, UserModel): ident = request.auth else: ident = self.get_ident(request) return self.cache_format % { 'scope': self.scope, 'ident': ident, } class AddressThrottle(SimpleRateThrottle): scope = 'address' def get_cache_key(self, request, view): if isinstance(request.user, UserModel): ident = request.auth else: ident = self.get_ident(request) return self.cache_format % { 'scope': self.scope, 'ident': ident, }
settings.py
# 节流配置,登录用户每分钟五次请求 REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': ( 'App.throttles.UserThrottle', ), 'DEFAULT_THROTTLE_RATES': { # 每分钟五次, user 和 address 对应throttles中的 scope 'user': '5/m', 'address': '10/m', } }
auth.py
from django.core.cache import cache from rest_framework.authentication import BaseAuthentication from App.models import UserModel # 登录认证 class LoginAuthentication(BaseAuthentication): def authenticate(self, request): # if request.method == 'GET': try: token = request.query_params.get('token') user_id = cache.get(token) user = UserModel.objects.get(pk=user_id) return user, token except Exception: return
permissions.py
from rest_framework.permissions import BasePermission from App.models import UserModel # 以是否登录来判断是否具备权限 class RequireLoginPermission(BasePermission): def has_permission(self, request, view): print(request.user) return isinstance(request.user, UserModel)
serializers.py
from rest_framework import serializers from App.models import UserModel, Address class AddressSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Address fields = ('url', 'id', 'a_address') class UserSerializer(serializers.HyperlinkedModelSerializer): # 查询用户时获取该用户地址 # address_set = AddressSerializer(many=True, read_only=True) # 默认级联字段是 address_set ,可以修改模型实现替换 related_name 关联名 address_list = AddressSerializer(many=True, read_only=True) class Meta: model = UserModel fields = ('url', 'id', 'u_name', 'u_password', 'address_list')
views.py
import uuid from django.core.cache import cache from rest_framework import exceptions, viewsets, status from rest_framework.generics import CreateAPIView, RetrieveAPIView from rest_framework.response import Response from App.auth import LoginAuthentication from App.models import UserModel, Address from App.permissions import RequireLoginPermission from App.serializers import UserSerializer, AddressSerializer # 用户注册登录 from App.throttles import UserThrottle class UsersAPIView(CreateAPIView): serializer_class = UserSerializer queryset = UserModel.objects.all() def post(self, request, *args, **kwargs): action = request.query_params.get('action') if action == "login": u_name = request.data.get('u_name') u_password = request.data.get('u_password') try: user = UserModel.objects.get(u_name=u_name) if user.u_password != u_password: raise exceptions.AuthenticationFailed token = uuid.uuid4().hex cache.set(token, user.id, timeout=60*60*24) data = { 'msg': 'login success', 'status': 200, 'token': token, } return Response(data) except UserModel.DoesNotExist: raise exceptions.NotFound elif action == "register": return self.create(request, *args, **kwargs) else: raise exceptions.ParseError class UserAPIView(RetrieveAPIView): serializer_class = UserSerializer queryset = UserModel.objects.all() authentication_classes = (LoginAuthentication,) permission_classes = (RequireLoginPermission,) # 节流只针对 UserAPIView 生效 [存疑??] # throttle_classes = (UserThrottle,) # throttle_scope = '10/m' # 重写 RetrieveModelMixin 中 retrieve # 实现当 token的id 和 用户的id 一致时只会查询到自己的信息 def retrieve(self, request, *args, **kwargs): # 提前判断用户 if kwargs.get('pk') != request.user.id: raise exceptions.AuthenticationFailed instance = self.get_object() # if instance.id != request.user.id: # raise exceptions.AuthenticationFailed serializer = self.get_serializer(instance) return Response(serializer.data) class AddressAPIView(viewsets.ModelViewSet): serializer_class = AddressSerializer queryset = Address.objects.all() # 登录认证 authentication_classes = (LoginAuthentication,) # 添加权限 permission_classes = (RequireLoginPermission,) def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) self.perform_create(serializer) headers = self.get_success_headers(serializer.data) user = request.user # 得到地址id,绑定user,保存 a_id = serializer.data.get('id') address = Address.objects.get(pk=a_id) address.a_user = user address.save() return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) # 重写ListModelMixin中list方法,实现登录用户只能GET到自己的地址 def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.queryset.filter(a_user=request.user)) page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) return Response(serializer.data)
urls.py
from django.urls import path from App import views urlpatterns = [ path('users/', views.UsersAPIView.as_view()), path('users/<int:pk>/', views.UserAPIView.as_view(), name='usermodel-detail'), path('address/', views.AddressAPIView.as_view( { 'post': 'create', 'get': 'list', } )), path('address/<int:pk>/', views.AddressAPIView.as_view( { 'get': 'retrieve', } ), name='address-detail'), ]
从现在开始,种下梦想中的参天大树