• python-Django-rest_framework用户级联及用户访问节流


    models.py

    # models.py
    from django.db import models
    
    # 用户表
    class User(models.Model):
        u_name = models.CharField(max_length=32, unique=True)
        u_password = models.CharField(max_length=256)
    
    
    # 地址表
    class Address(models.Model):
        a_address = models.CharField(max_length=128)
        # null=True 允许外键为空
        a_user = models.ForeignKey(User, on_delete=True, null=True)

    views.py

    # views.py
    import uuid
    
    from django.core.cache import cache
    from django.shortcuts import render
    
    # Create your views here.
    from rest_framework import exceptions, viewsets, status
    from rest_framework.generics import CreateAPIView, RetrieveAPIView
    from rest_framework.response import Response
    
    from myapp.auth import LoginAuthentication
    from myapp.models import User, Address
    from myapp.permissions import LoginPermissions
    from myapp.serializers import UserSerializer, AddressSerializer
    
    
    def test(request):
        return render(request, 'test.html')
    
    
    # 用户的创建及登录
    class UsersAPIView(CreateAPIView):
        serializer_class = UserSerializer
        queryset = User.objects.all()
    
        # 登录和注册都是post请求
        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 = User.objects.get(u_name=u_name)
                    if user.u_password == u_password:
                        token = uuid.uuid4().hex
                        cache.set(token, user.id)
                        data = {
                            'msg': 'ok',
                            'status': 200,
                            'token': token
                        }
                        return Response(data)
                    else:
                        raise exceptions.AuthenticationFailed  # 密码错误
                except User.DoesNotExist:
                    raise exceptions.AuthenticationFailed  # 用户名错误
            elif action == 'register':
                return self.create(request, *args, **kwargs)
            else:
                raise exceptions.ParseError    # 既不是登录也不是注册
    
    
    # 单个用户,只用于展示
    class UserAPIView(RetrieveAPIView):
        serializer_class = UserSerializer
        queryset = User.objects.all()
        # 添加地址前需要进行用户认证
        authentication_classes = (LoginAuthentication,)
        # 权限控制
        permission_classes = (LoginPermissions,)
    
        # 验证登录后只能获取当前用户的数据
        # 判定用户数据只能是用户登录的用户数据,不能获取其他用户的用户数据
        # RetrieveAPIView->get->retrieve
        def retrieve(self, request, *args, **kwargs):
            # instance = self.get_object()
            # # 进行了数据库查询操作
            # if instance.id != request.user.id:
            #     raise exceptions.AuthenticationFailed   # 当前已登录用户的id和要获取值id不一致
            # serializer = self.get_serializer(instance)
            # return Response(serializer.data)
    
            # 在路径中拿到id值
            if kwargs.get('pk') != request.user.id:   # 也可在中间件中进行验证
                raise exceptions.AuthenticationFailed   # 当前已登录用户的id和要获取值id不一致
            instance = self.get_object()
            serializer = self.get_serializer(instance)
            return Response(serializer.data)
    
    
    # 地址的增删改查
    class AddressesAPIView(viewsets.ModelViewSet):
        serializer_class = AddressSerializer
        queryset = Address.objects.all()
        # 添加地址前需要进行用户认证
        authentication_classes = (LoginAuthentication,)
        # 权限控制
        permission_classes = (LoginPermissions,)
    
        # 重写create,在创建的时候就关联用户
        def create(self, request, *args, **kwargs):
            # 地址的创建
            serializer = self.get_serializer(data=request.data)
            serializer.is_valid(raise_exception=True)
            self.perform_create(serializer)
            # 拿到用户
            user = request.user
            # 拿到对应地址的id
            a_id = serializer.data.get('id')
            # 把是哪个用户绑定到地址表的a_user中
            address = Address.objects.get(pk=a_id)
            address.a_user = user
            address.save()
    
            headers = self.get_success_headers(serializer.data)
            return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
    
        # 实现用户数据过滤
        def list(self, request, *args, **kwargs):
            # queryset = self.filter_queryset(self.get_queryset())
            # 用户数据过滤,queryset为序列化后的数据
            queryset = self.filter_queryset(self.queryset.filter(a_user=request.user))
            serializer = self.get_serializer(queryset, many=True)
            return Response(serializer.data)
    
    
    class AddressAPIView(viewsets.ModelViewSet):
        serializer_class = AddressSerializer
        queryset = Address.objects.all()

    urls.py

    # urls.py
    from django.urls import path
    from . import views
    
    app_name = 'my_app'
    urlpatterns = [
        path('test/', views.test),
        path('user/', views.UsersAPIView.as_view()),
        path('user/<int:pk>/', views.UserAPIView.as_view()),
        path('address/', views.AddressesAPIView.as_view({
            'post': 'create',
            'get': 'list',
        })),
        path('address/<int:pk>/', views.AddressAPIView.as_view({
            'get': 'retrieve',
        })),
    ]

    serializers.py

    # 序列化
    from rest_framework import serializers
    
    # serializers.py
    from myapp.models import User, Address
    
    
    # 地址表序列化
    class AddressSerializer(serializers.ModelSerializer):
        class Meta:
            model = Address
            fields = ('id', 'a_address')
    
    
    # 用户表序列化
    class UserSerializer(serializers.ModelSerializer):
        # 级联数据的级联显示
        # address_set 是User模型的隐形属性
        # 若这里想自定义名称,我们可以在Address模型的外键关联中添加related_name属性自定义名称(related_name='custom-name')
        address_set = AddressSerializer(many=True, read_only=True)
    
        class Meta:
            model = User
            fields = ('id', 'u_name', 'u_password','address_set')

    permissions.py

    # permissions.py
    # 权限控制
    from rest_framework.permissions import BasePermission
    
    from myapp.models import User
    
    
    class LoginPermissions(BasePermission):
        def has_permission(self, request, view):
            # 验证用户是否在用户列表里
            if isinstance(request.user, User):
                return True
            return False

    auth.py

    # auth.py
    # 用户认证
    from django.core.cache import cache
    from rest_framework.authentication import BaseAuthentication
    
    from myapp.models import User
    
    
    class LoginAuthentication(BaseAuthentication):
        def authenticate(self, request):
            # 地址的增删改查都需要用户认证
            # if request.method == 'GET':
            try:
                token = request.query_params.get('token')
                u_id = cache.get(token)
                user = User.objects.get(pk=u_id)
                return user, token
            except:
                return

    throttles.py

    # throttles.py
    # 节流
    from rest_framework.throttling import SimpleRateThrottle
    
    from myapp.models import User
    
    
    class UserThrottle(SimpleRateThrottle):
        scope = 'user'
    
        def get_cache_key(self, request, view):
            # if request.user.is_authenticated:
            if isinstance(request.user, User):   # 若用户存在
                # ident = request.user.pk
                ident = request.auth   # 将auth作为唯一标识
    
            else:
                ident = self.get_ident(request)
    
            return self.cache_format % {
                'scope': self.scope,
                'ident': ident
            }

    setttings.py

    # 节流的全局配置
    REST_FRAMEWORK ={
        # APIView ->throttle_classes->api_settings->DEFAULTS->DEFAULT_THROTTLE_CLASSES
        'DEFAULT_THROTTLE_CLASSES':[
            'myapp.throttles.UserThrottle'   # 找到包下对应的节流器
        ],
        'DEFAULT_THROTTLE_RATES': {
            # throttling -> SimpleRateThrottle —> parse_rate -> duration
            'user': '5/m'   # 一分钟可以访问5次
        }
    }
  • 相关阅读:
    偶串_牛客网
    制造回文_牛客网
    字典树(前缀树)的实现
    动态规划LeetCode174地下城游戏
    动态规划LeetCode64最小路径和
    动态规划LeetCode300最长上升子序列
    动态规划LeetCode120三角形最小路径和
    Zabbix 监控sqlserver
    如何回收VCSA 6自带的vPostgres数据库空间
    领益科技:导出Wireless组中的成员
  • 原文地址:https://www.cnblogs.com/Vera-y/p/12108068.html
Copyright © 2020-2023  润新知