• django rest framework 学习笔记-实战商城


     01项目环境搭建_哔哩哔哩_bilibili  本博客借鉴至大佬的视频学习笔记


    1. # 创建项目
    2. django-admin startproject MyShop
    3. # 创建app
    4. E:\desktop\my_drf\MyShop>django-admin startapp goods
    5. E:\desktop\my_drf\MyShop>django-admin startapp order
    6. E:\desktop\my_drf\MyShop>django-admin startapp cart
    7. E:\desktop\my_drf\MyShop>django-admin startapp users

    创建apps文件夹放入的上面应用

    1. # 注册应用
    2. 'rest_framework',
    3. 'corsheaders',
    4. 'apps.users',
    5. 'apps.goods',
    6. 'apps.cart',
    7. 'apps.order'
    8. # 配置mysql
    9. DATABASES = {
    10. 'default': {
    11. 'ENGINE': 'django.db.backends.mysql',
    12. 'NAME': 'shop',
    13. 'USER': 'root',
    14. 'PASSWORD': 'pass',
    15. 'HOST': 'localhost',
    16. 'PORT': 3306
    17. }
    18. }
    19. # 允许所有的用户跨域请求
    20. CORS_ORIGIN_ALLOW_ALL = True
    21. # 中文及时区调整
    22. LANGUAGE_CODE = 'zh-hans'
    23. TIME_ZONE = 'Asia/Shanghai'

     创建shop数据库

    mysql> create database shop charset=utf8;
    Query OK, 1 row affected, 1 warning (0.01 sec)

     公共表设计:

    1. # 定义继承时,需要setting自定义用户类模型
    2. AUTH_USER_MODEL = 'users.User'
    1. # 创建 common目录下db.py文件
    2. from django.db import models
    3. class BaseModel(models.Model):
    4. """ 抽象的模型基类,定义公共模型字段 """
    5. create_time = models.DateTimeField(auto_now_add=True,verbose_name='创建时间')
    6. update_time = models.DateTimeField(auto_now=True,verbose_name='更新时间')
    7. is_delete = models.BooleanField(default=False,verbose_name='删除标记')
    8. class Meta:
    9. # 声明这个是抽象模型,生成迁移文件时,不会在数据库生成表
    10. abstract = True
    11. verbose_name_plural = '公共字段表'
    12. db_table = 'BaseTabel'

    用户表结构设计

    1. from django.db import models
    2. from django.contrib.auth.models import AbstractUser # django自带的用户认证模型
    3. from common.db import BaseModel
    4. # Create your models here.
    5. class User(AbstractUser,BaseModel):
    6. """用户表"""
    7. mobile = models.CharField(verbose_name='手机号',help_text='手机号',max_length=11,default='',blank=True)
    8. avatar = models.ImageField(verbose_name='用户头像',help_text='用户头像',max_length=30,null=True,blank=True)
    9. class Meta:
    10. db_table = 'users'
    11. verbose_name = '用户表'
    12. class Addr(models.Model):
    13. """收货地址模型"""
    14. user = models.ForeignKey('User',verbose_name='所属用户',on_delete=models.CASCADE)
    15. phone = models.CharField(verbose_name='手机号',help_text='手机号',max_length=11,null=True,blank=True)
    16. name = models.CharField(verbose_name='联系人',help_text='联系人',max_length=20,null=True,blank=True)
    17. province = models.CharField(verbose_name='省份',help_text='省份',max_length=20,null=True,blank=True)
    18. city = models.CharField(verbose_name='城市',help_text='城市',max_length=20,null=True,blank=True)
    19. country = models.CharField(verbose_name='区县',help_text='区县',max_length=20,null=True,blank=True)
    20. is_default = models.BooleanField(verbose_name='是否为默认地址',help_text='省份',max_length=30,default=False)
    21. class Meta:
    22. db_table = 'addr'
    23. verbose_name = '收货地址表'
    24. class Area(models.Model):
    25. """省市区县地址模型"""
    26. pid = models.ImageField(verbose_name='上级ID',help_text='上级ID',max_length=20,null=True,blank=True)
    27. name = models.CharField(verbose_name='地区名',help_text='地区名',max_length=20,null=True,blank=True)
    28. level = models.CharField(verbose_name='区域等级',help_text='区域等级',max_length=20,null=True,blank=True)
    29. class Meta:
    30. db_table = 'area'
    31. verbose_name = '地区表'
    32. class VerifyCode(models.Model):
    33. """验证码模型"""
    34. mobile = models.CharField(verbose_name='手机号码',help_text='手机号码',max_length=11,null=True,blank=True)
    35. code = models.CharField(verbose_name='验证码',help_text='验证码',max_length=6,null=True,blank=True)
    36. create_time = models.DateTimeField(auto_now_add=True,verbose_name='生成时间',help_text='生成时间')
    37. class Meta:
    38. db_table = 'verifycode'
    39. verbose_name = '手机验证码表'

     执行迁移文件,生成表结构

    E:\desktop\my_drf\MyShop>python manage.py makemigrations
    Migrations for 'users':
      apps\users\migrations\0001_initial.py
        - Create model User
        - Create model Area
        - Create model VerifyCode
        - Create model Addr

    E:\desktop\my_drf\MyShop>python manage.py migrate

     结果展示:

     用户鉴权:使用JWT认证

    创建超级管理员用户

    1. E:\desktop\my_drf\MyShop>python manage.py createsuperuser
    2. 用户名: pass
    3. 电子邮件地址: pass@qq.com
    4. Password:
    5. Password (again):

    JWT权限认证配置

    1. # 注册jwt
    2. 'rest_framework_simplejwt',
    3. # 配置setting
    4. REST_FRAMEWORK = {
    5. # DRF配置鉴权方式
    6. 'DEFAULT_AUTHENTICATION_CLASSES': (
    7. 'rest_framework_simplejwt.authentication.JWTAuthentication',
    8. 'rest_framework.authentication.BasicAuthentication', # Basic 认证
    9. 'rest_framework.authentication.SessionAuthentication', # Session 认证
    10. ),
    11. }
    12. # 配置总路由urls
    13. path('api/users/',include('apps.users.urls'))
    14. # user url 配置
    15. from rest_framework_simplejwt.views import TokenVerifyView, TokenRefreshView, TokenObtainPairView
    16. urlpatterns = [
    17. path('login/', TokenObtainPairView.as_view(), name='login'), # 登录
    18. path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), # token 刷新
    19. path('token/verify/', TokenVerifyView.as_view(), name='token_verify'), # token 效验
    20. ]

     配置Postman环境变量

    Postman运行示图:

     自定义用户登录功能的实现

    1. from rest_framework_simplejwt.views import TokenObtainPairView
    2. # Create your views here.
    3. class LoginView(TokenObtainPairView):
    4. def post(self, request, *args, **kwargs):
    5. serializer = self.get_serializer(data=request.data)
    6. try:
    7. serializer.is_valid(raise_exception=True)
    8. except TokenError as e:
    9. raise InvalidToken(e.args[0])
    10. # 自定义登录成功后返回的数据信息
    11. result = serializer.validated_data
    12. result['id'] = serializer.user.id
    13. result['email'] = serializer.user.email
    14. result['mobile'] = serializer.user.mobile
    15. result['username'] = serializer.user.username
    16. result['token'] = result.pop('access')
    17. return Response(serializer.validated_data, status=status.HTTP_200_OK)

    运行结果:

    用户注册功能的实现

    1. import re
    2. from rest_framework import status
    3. from rest_framework.response import Response
    4. from rest_framework_simplejwt.exceptions import TokenError, InvalidToken
    5. from rest_framework_simplejwt.views import TokenObtainPairView
    6. from rest_framework.views import APIView
    7. from .models import User
    8. # Create your views here.
    9. class RegisterView(APIView):
    10. def post(self,request):
    11. """注册:接收参数并校验、创建用户 """
    12. username = request.data.get('username')
    13. password = request.data.get('password')
    14. email = request.data.get('email')
    15. password_confirmation = request.data.get('password_confirmation')
    16. # 检验
    17. print(all([username,password,email,password_confirmation]))
    18. print([username,password,email,password_confirmation])
    19. if not all([username,password,email,password_confirmation]):
    20. return Response({'error':'所有参数不能为空'},status=status.HTTP_422_UNPROCESSABLE_ENTITY)
    21. # 检验用户是否已存在
    22. if User.objects.filter(username=username).exists():
    23. return Response({'error':'用户名已存在'},status=status.HTTP_422_UNPROCESSABLE_ENTITY)
    24. # check password
    25. if password !=password_confirmation:
    26. return Response({'error':'两次输入的密码不一致'},status=status.HTTP_422_UNPROCESSABLE_ENTITY)
    27. if not (6<len(password)<18):
    28. return Response({'error':'密码长度需要在6-18位之间'},status=status.HTTP_422_UNPROCESSABLE_ENTITY)
    29. # check email
    30. if User.objects.filter(email=email).exists():
    31. return Response({'error':'该邮箱已被他人注册'},status=status.HTTP_422_UNPROCESSABLE_ENTITY)
    32. if not re.match(r'^[a-z0-9][\w.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$',email):
    33. return Response({'error':'邮箱格式有误'},status=status.HTTP_422_UNPROCESSABLE_ENTITY)
    34. # 创建用户
    35. obj = User.objects.create_user(username=username,email=email,password=password)
    36. res = {
    37. 'username':username,
    38. 'id':obj.id,
    39. 'email':obj.email
    40. }
    41. return Response(res,status=status.HTTP_201_CREATED)
    42. # url 配置
    43. path('register/', RegisterView.as_view(), name='register'), # 注册

    多字段用户登录功能支持

    1. # 规范化格式,创建authentication.py放于common目录下
    2. from django.contrib.auth.backends import ModelBackend
    3. from apps.users.models import User
    4. from django.db.models import Q
    5. from rest_framework import serializers
    6. class Authentication(ModelBackend):
    7. """自定义用户登录的认证类,实现多字段登录"""
    8. def authenticate(self, request, username=None, password=None, **kwargs):
    9. """支持使用手机号/邮箱/用户名登录"""
    10. try:
    11. user = User.objects.get(Q(username=username) | Q(mobile=username) | Q(email=username))
    12. except:
    13. raise serializers.ValidationError({'error':'未找到该用户!'})
    14. # check password
    15. if user.check_password(password):
    16. return user
    17. raise serializers.ValidationError({'error':'密码错误'})
    18. # 配置setting 使用
    19. # 使用自定义的认证类进行身份登录,登录时验证信息
    20. AUTHENTICATION_BACKENDS =[
    21. 'common.authentication.Authentication',
    22. ]

    注意上述代码使用 User 的导包方式

    from apps.users.models import User √

    from django.contrib.auth.models import User ×

    因当前在setting设置的规范 AUTH_USER_MODEL = 'users.User'

    第二行代码可能会导致找不到用户而抛出异常,此时的解决方法

    from django.contrib.auth.models import User
    from django.contrib.auth import get_user_model
    
    User= get_user_model()  # 重写获取注册的app模型类

    运行结果展示:

    刷新 jwt token: 当上述token失效时可以通过刷新refresh得到新的token

    要求:检查参数不为空、检验密码账号正确、登录成功返回token,支持多字段登录

    1. # 配置urls文件
    2. from rest_framework_simplejwt.views import TokenRefreshView,TokenVerifyView
    3. urlpatterns = [
    4. path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), # 刷新token
    5. path('token/verify/', TokenVerifyView.as_view(), name='token_verify'), # 检验token
    6. ]

     获取用户信息,要求如下

    • 获取用户信息时进行权限检验,需要在请求中添加认证字段
    • 防止越权篡改数据

    1. # 定义序列化器
    2. from rest_framework import serializers
    3. from apps.users.models import User
    4. class UserSerializer(serializers.ModelSerializer):
    5. """用户的模型序列化器"""
    6. class Meta:
    7. model = User
    8. fields = ['id','username','email','mobile','avatar','last_name']
    9. # 定义视图
    10. from .models import User
    11. from .serializers import UserSerializer
    12. from rest_framework.viewsets import GenericViewSet,mixins
    13. # Create your views here.
    14. class UserView(GenericViewSet,mixins.RetrieveModelMixin):
    15. """用户相关的操作视图集"""
    16. queryset = User.objects.all()
    17. serializer_class = UserSerializer
    18. # 配置urls
    19. path('users//',UserView.as_view({'get':'retrieve'}), name='user_get'), # 检验token

     运行结果展示:

    增加权限管理,防止当前用户访问其他用户

    1. from rest_framework import permissions
    2. class UserPermissions(permissions.BasePermission):
    3. """
    4. Custom permission to only allow owners of an object to edit it.
    5. """
    6. def has_object_permission(self, request, view, obj):
    7. # 判断是否是管理员
    8. if request.user.is_superuser:
    9. return True
    10. # 判断当前的用户对象和登录的用户对象是否是同一个,防止越权
    11. return obj == request.user
    12. # 更新配置视图文件
    13. permission_classes = [IsAuthenticated,UserPermissions] # 设置权限认证

    更多详细权限配置:4 - Authentication and permissions - Django REST framework

     上传用户头像-

    要求:检验参数avatar、文件大小不超过300kb、返回的url可以访问图片,防止用户越权

    1. # 头像文件上传视图
    2. class UserView(GenericViewSet,mixins.RetrieveModelMixin):
    3. """用户相关的操作视图集"""
    4. queryset = User.objects.all()
    5. serializer_class = UserSerializer
    6. permission_classes = [IsAuthenticated,UserPermissions] # 设置权限认证
    7. def upload_avatar(self,request,*args,**kwargs):
    8. """上传头像"""
    9. obj= self.get_object()
    10. avatar = request.data.get('avatar')
    11. if not avatar:
    12. return Response({'error':'上传文件不可为空!'},status=status.HTTP_400_BAD_REQUEST)
    13. size = avatar.size
    14. if size >1024*300:
    15. return Response({'error': '上传文件大小不可超过300kb!'}, status=status.HTTP_400_BAD_REQUEST)
    16. # partial 只对部分字段(上传的字段)进行校验
    17. serializer = self.get_serializer(obj,data={'avatar':avatar},partial=True)
    18. serializer.is_valid(raise_exception=True)
    19. serializer.save()
    20. return Response({'url':serializer.data['avatar']})
    21. # setting配置
    22. # 文件上传的路径
    23. MEDIA_ROOT = BASE_DIR / 'file/image'
    24. # 文件的url路径
    25. MEDIA_URL = 'file/image/'
    26. # 配置url
    27. path('/avatar/upload/',UserView.as_view({'post':'upload_avatar'}), name='avatar_post')

    头像文件获取

    1. # 头像文件获取
    2. class FileView(APIView):
    3. def get(self,request,name):
    4. path = MEDIA_ROOT / name
    5. if os.path.isfile(path):
    6. return FileResponse(open(path,'rb'))
    7. return Response({"error":'没有找到该文件!'},status=status.HTTP_400_BAD_REQUEST)
    8. # 配置总路由url
    9. re_path(r'file/image/(.+?)/', FileView.as_view()),

    文件上传更多知识:文件上传 | Django 文档 | Django

  • 相关阅读:
    STC89C51基础及项目第14天:循迹小车、跟随小车
    HTML+CSS
    Linux系统编程:文件描述符以及IO多路复用
    【JavaSE】内部类总结
    10年的老测试告诉你八大测试用例设计方法
    Linux账号管理:用户账号与用户组
    FPGA备战秋招---常用知识点
    《HelloGitHub》第 90 期
    使用正则表达式在中英文之间添加空格
    A-LOAM及禾赛LiDAR调试记录(上)
  • 原文地址:https://blog.csdn.net/qq_44238024/article/details/136199703