Django项目总结: REST Framework 用户注册登录,权限控制,级联操作查询,节流控制自定义

用户注册登录,权限控制,级联操作查询,节流控制自定义

需求

  • 存在级联数据
  • 用户和收货地址
  • 节流

分析

  • 数据开始
    • 模型定义
    • 用户和收货地址 一对多
      • 用户表
      • 地址表
        • ForeignKey
    • 序列化
      • 级联数据如何实现序列化???
    • 节流

节流器

  • BaseThrottle
    • allow_request
      • 是否允许的请求的核心
    • get_ident
      • 获取客户端唯一标识
    • wait
  • SimpleRateThrottle
    • get_cache_key
      • 获取缓存标识
    • get_rate
      • 获取频率
    • parse_rate
      • 转换频率
        • num/duration
          • duration
            • s
            • m
            • h
            • d
    • allow_request
      • 是否允许请求
      • 重写的方法
    • throttle_success
      • 允许请求,进行请求记录
    • throttle_failure
      • 不允许请求
    • wait
      • 还有多少时间之后允许
  • AnonRateThrottle
    • get_cache_key
      • 获取缓存key的原则
  • UserRateThrottle
    • 和上面一模一样
  • ScopedRateThrottle
    • 和上面一样
    • 多写了从属性中获取频率

技能点

  • HTTP_X_FORWARDED_FOR
    • 获取你的原始IP
      • 通过的普通的代理发送请求的请求
      • 如果获取REMOTE_ADDR获取到的是代理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'),
]
原文地址:https://www.cnblogs.com/dc2019/p/13493573.html