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

news2024/10/7 11:23:48

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


# 创建项目
django-admin startproject MyShop

# 创建app
E:\desktop\my_drf\MyShop>django-admin startapp goods

E:\desktop\my_drf\MyShop>django-admin startapp order

E:\desktop\my_drf\MyShop>django-admin startapp cart

E:\desktop\my_drf\MyShop>django-admin startapp users

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

# 注册应用
'rest_framework',
'corsheaders',
'apps.users',
'apps.goods',
'apps.cart',
'apps.order'

# 配置mysql
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'shop',
        'USER': 'root',
        'PASSWORD': 'pass',
        'HOST': 'localhost',
        'PORT': 3306
    }
}

# 允许所有的用户跨域请求
CORS_ORIGIN_ALLOW_ALL = True

# 中文及时区调整
LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'


 创建shop数据库

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

 公共表设计:

# 定义继承时,需要setting自定义用户类模型
AUTH_USER_MODEL = 'users.User'
# 创建 common目录下db.py文件
from django.db import models

class BaseModel(models.Model):
    """ 抽象的模型基类,定义公共模型字段 """

    create_time = models.DateTimeField(auto_now_add=True,verbose_name='创建时间')
    update_time = models.DateTimeField(auto_now=True,verbose_name='更新时间')
    is_delete = models.BooleanField(default=False,verbose_name='删除标记')

    class Meta:
        # 声明这个是抽象模型,生成迁移文件时,不会在数据库生成表
        abstract = True
        verbose_name_plural = '公共字段表'
        db_table = 'BaseTabel'

用户表结构设计

from django.db import models
from django.contrib.auth.models import AbstractUser  # django自带的用户认证模型
from common.db import BaseModel
# Create your models here.

class User(AbstractUser,BaseModel):
    """用户表"""
    mobile = models.CharField(verbose_name='手机号',help_text='手机号',max_length=11,default='',blank=True)
    avatar = models.ImageField(verbose_name='用户头像',help_text='用户头像',max_length=30,null=True,blank=True)

    class Meta:
        db_table = 'users'
        verbose_name = '用户表'

class Addr(models.Model):
    """收货地址模型"""
    user = models.ForeignKey('User',verbose_name='所属用户',on_delete=models.CASCADE)
    phone = models.CharField(verbose_name='手机号',help_text='手机号',max_length=11,null=True,blank=True)
    name = models.CharField(verbose_name='联系人',help_text='联系人',max_length=20,null=True,blank=True)
    province = models.CharField(verbose_name='省份',help_text='省份',max_length=20,null=True,blank=True)
    city = models.CharField(verbose_name='城市',help_text='城市',max_length=20,null=True,blank=True)
    country = models.CharField(verbose_name='区县',help_text='区县',max_length=20,null=True,blank=True)
    is_default = models.BooleanField(verbose_name='是否为默认地址',help_text='省份',max_length=30,default=False)

    class Meta:
        db_table = 'addr'
        verbose_name = '收货地址表'

class Area(models.Model):
    """省市区县地址模型"""
    pid = models.ImageField(verbose_name='上级ID',help_text='上级ID',max_length=20,null=True,blank=True)
    name = models.CharField(verbose_name='地区名',help_text='地区名',max_length=20,null=True,blank=True)
    level = models.CharField(verbose_name='区域等级',help_text='区域等级',max_length=20,null=True,blank=True)

    class Meta:
        db_table = 'area'
        verbose_name = '地区表'

class VerifyCode(models.Model):
    """验证码模型"""
    mobile = models.CharField(verbose_name='手机号码',help_text='手机号码',max_length=11,null=True,blank=True)
    code = models.CharField(verbose_name='验证码',help_text='验证码',max_length=6,null=True,blank=True)
    create_time = models.DateTimeField(auto_now_add=True,verbose_name='生成时间',help_text='生成时间')

    class Meta:
        db_table = 'verifycode'
        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认证

创建超级管理员用户

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

JWT权限认证配置

# 注册jwt
'rest_framework_simplejwt',

# 配置setting
REST_FRAMEWORK = {
    # DRF配置鉴权方式
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
        'rest_framework.authentication.BasicAuthentication',  # Basic 认证
        'rest_framework.authentication.SessionAuthentication',  # Session 认证
    ),
}

# 配置总路由urls

path('api/users/',include('apps.users.urls'))

# user url 配置
from rest_framework_simplejwt.views import TokenVerifyView, TokenRefreshView, TokenObtainPairView

urlpatterns = [
    path('login/', TokenObtainPairView.as_view(), name='login'),  # 登录
    path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),  # token 刷新
    path('token/verify/', TokenVerifyView.as_view(), name='token_verify'),  # token 效验
]

 配置Postman环境变量

Postman运行示图:

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

from rest_framework_simplejwt.views import TokenObtainPairView

# Create your views here.
class LoginView(TokenObtainPairView):
    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)

        try:
            serializer.is_valid(raise_exception=True)
        except TokenError as e:
            raise InvalidToken(e.args[0])
        # 自定义登录成功后返回的数据信息
        result = serializer.validated_data
        result['id'] = serializer.user.id
        result['email'] = serializer.user.email
        result['mobile'] = serializer.user.mobile
        result['username'] = serializer.user.username
        result['token'] = result.pop('access')

        return Response(serializer.validated_data, status=status.HTTP_200_OK)

运行结果:

用户注册功能的实现

import re
from rest_framework import status
from rest_framework.response import Response
from rest_framework_simplejwt.exceptions import TokenError, InvalidToken
from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework.views import APIView
from .models import User
# Create your views here.
class RegisterView(APIView):
    def post(self,request):
        """注册:接收参数并校验、创建用户 """
        username = request.data.get('username')
        password = request.data.get('password')
        email = request.data.get('email')
        password_confirmation = request.data.get('password_confirmation')
        # 检验
        print(all([username,password,email,password_confirmation]))
        print([username,password,email,password_confirmation])
        if not all([username,password,email,password_confirmation]):
            return Response({'error':'所有参数不能为空'},status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        # 检验用户是否已存在
        if User.objects.filter(username=username).exists():
            return Response({'error':'用户名已存在'},status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        # check password
        if password !=password_confirmation:
            return Response({'error':'两次输入的密码不一致'},status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        if not (6<len(password)<18):
            return Response({'error':'密码长度需要在6-18位之间'},status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        # check email
        if User.objects.filter(email=email).exists():
            return Response({'error':'该邮箱已被他人注册'},status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        if not re.match(r'^[a-z0-9][\w.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$',email):
            return Response({'error':'邮箱格式有误'},status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        # 创建用户
        obj = User.objects.create_user(username=username,email=email,password=password)
        res = {
            'username':username,
            'id':obj.id,
            'email':obj.email
        }
        return Response(res,status=status.HTTP_201_CREATED)


# url 配置
path('register/', RegisterView.as_view(), name='register'),  # 注册

多字段用户登录功能支持

# 规范化格式,创建authentication.py放于common目录下

from django.contrib.auth.backends import ModelBackend
from apps.users.models import User
from django.db.models import Q
from rest_framework import serializers

class Authentication(ModelBackend):
    """自定义用户登录的认证类,实现多字段登录"""
    def authenticate(self, request, username=None, password=None, **kwargs):
        """支持使用手机号/邮箱/用户名登录"""
        try:
            user = User.objects.get(Q(username=username) | Q(mobile=username) | Q(email=username))
        except:
            raise serializers.ValidationError({'error':'未找到该用户!'})
            # check password
        if user.check_password(password):
            return user
        raise serializers.ValidationError({'error':'密码错误'})


# 配置setting 使用
# 使用自定义的认证类进行身份登录,登录时验证信息
AUTHENTICATION_BACKENDS =[
    'common.authentication.Authentication',
]

注意上述代码使用 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,支持多字段登录

# 配置urls文件
from rest_framework_simplejwt.views import TokenRefreshView,TokenVerifyView

urlpatterns = [
    path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),  # 刷新token
    path('token/verify/', TokenVerifyView.as_view(), name='token_verify'),  # 检验token
]

 获取用户信息,要求如下

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

 

# 定义序列化器
from rest_framework import serializers
from apps.users.models import User

class UserSerializer(serializers.ModelSerializer):
    """用户的模型序列化器"""
    class Meta:
        model = User
        fields = ['id','username','email','mobile','avatar','last_name']

# 定义视图
from .models import User
from .serializers import UserSerializer
from rest_framework.viewsets import GenericViewSet,mixins

# Create your views here.
class UserView(GenericViewSet,mixins.RetrieveModelMixin):
    """用户相关的操作视图集"""
    queryset = User.objects.all()
    serializer_class = UserSerializer

# 配置urls
path('users/<int:pk>/',UserView.as_view({'get':'retrieve'}), name='user_get'),  # 检验token


 运行结果展示:

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

from rest_framework import permissions
class UserPermissions(permissions.BasePermission):
    """
    Custom permission to only allow owners of an object to edit it.
    """
    def has_object_permission(self, request, view, obj):
        # 判断是否是管理员
        if request.user.is_superuser:
            return True
        # 判断当前的用户对象和登录的用户对象是否是同一个,防止越权
        return obj == request.user


# 更新配置视图文件
permission_classes = [IsAuthenticated,UserPermissions] # 设置权限认证

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

 上传用户头像-

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

# 头像文件上传视图
class UserView(GenericViewSet,mixins.RetrieveModelMixin):
    """用户相关的操作视图集"""
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = [IsAuthenticated,UserPermissions] # 设置权限认证

    def upload_avatar(self,request,*args,**kwargs):
        """上传头像"""
        obj= self.get_object()
        avatar = request.data.get('avatar')
        if not avatar:
            return Response({'error':'上传文件不可为空!'},status=status.HTTP_400_BAD_REQUEST)
        size = avatar.size
        if size >1024*300:
            return Response({'error': '上传文件大小不可超过300kb!'}, status=status.HTTP_400_BAD_REQUEST)
        # partial 只对部分字段(上传的字段)进行校验
        serializer = self.get_serializer(obj,data={'avatar':avatar},partial=True)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response({'url':serializer.data['avatar']})

# setting配置
# 文件上传的路径
MEDIA_ROOT = BASE_DIR / 'file/image'
# 文件的url路径
MEDIA_URL = 'file/image/'


# 配置url
path('<int:pk>/avatar/upload/',UserView.as_view({'post':'upload_avatar'}), name='avatar_post')



头像文件获取

# 头像文件获取
class FileView(APIView):
    def get(self,request,name):
        path  = MEDIA_ROOT / name
        if os.path.isfile(path):
            return FileResponse(open(path,'rb'))
        return Response({"error":'没有找到该文件!'},status=status.HTTP_400_BAD_REQUEST)

# 配置总路由url
re_path(r'file/image/(.+?)/', FileView.as_view()),

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1462551.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++从入门到精通 第十七章(终极案例)

写在前面&#xff1a; 本系列专栏主要介绍C的相关知识&#xff0c;思路以下面的参考链接教程为主&#xff0c;大部分笔记也出自该教程&#xff0c;笔者的原创部分主要在示例代码的注释部分。除了参考下面的链接教程以外&#xff0c;笔者还参考了其它的一些C教材&#xff08;比…

互联网高科技公司领导AI工业化,MatrixGo加速人工智能落地

作者&#xff1a;吴宁川 AI&#xff08;人工智能&#xff09;工业化与AI工程化正在引领人工智能的大趋势。AI工程化主要从企业CIO角度&#xff0c;着眼于在企业生产环境中规模化落地AI应用的工程化举措&#xff1b;而AI工业化则从AI供应商的角度&#xff0c;着眼于以规模化方式…

Linux 权限详解

目录 一、权限的概念 二、权限管理 三、文件访问权限的相关设置方法 3.1chmod 3.2chmod ax /home/abc.txt 一、权限的概念 Linux 下有两种用户&#xff1a;超级用户&#xff08; root &#xff09;、普通用户。 超级用户&#xff1a;可以再linux系统下做任何事情&#xff…

程序媛的mac修炼手册-- 如何彻底卸载Python

啊&#xff0c;前段时间因为想尝试chatgpt的API&#xff0c;需要先创建一个python虚拟环境来安装OpenAI Python library. 结果&#xff0c;不出意外的出意外了&#xff0c;安装好OpenAI Python library后&#xff0c;因为身份认证问题&#xff0c;根本就没有获取API key的权限…

Apache Doris:从诞生到云原生时代的演进、技术亮点与未来展望

目录 前言 Apache Doris介绍 作者介绍 Apache Doris特性 Doris 数据流程 极简结构 高效自运维 高并发场景支持 MPP 执行引擎 明细与聚合模型的统一 便捷数据接入 Apache Doris 极速 1.0 时代 极速 列式内存布局 向量化的计算框架 Cache 亲和度 虚函数调用 SI…

Servlet(1)

文章目录 什么是ServletServlet 主要做的工作 第一个Servlet程序1.创建项目2. 引入依赖3. 创建目录1) 创建 webapp 目录2) 创建 web.xml3) 编写 web.xml 4. 编写代码5. 打包程序7. 验证程序 什么是Servlet Servlet 是一种实现动态页面的技术. 是一组 Tomcat 提供给程序猿的 AP…

Nginx配置组成与性能调优

目录 一、Nginx配置介绍 1. 模块组成 2. 图示 3. 相关框架 二. 配置调优 1. 全局配置 1.1 关闭版本和修改版本 1.2 修改启动的进程数 1.3 cpu与work进程绑定 1.4 pid路径 1.5 nginx进程的优先级&#xff08;work进程的优先级&#xff09; 1.6 调试work进程打开的文…

C++:static关键字

一、static成员变量(类变量、静态成员变量) 1、不属于类&#xff1b; 2、必须初始化&#xff1b; 3、同类中所有对象共享&#xff1b; 访问&#xff1a;类::类变量 &#xff0c; 对象.类变量 &#xff0c; 对象指针->类变量&#xff1b;底层都是类::类变量 …

3DSC特征描述符、对应关系可视化以及ICP配准

一、3DSC特征描述符可视化 C #include <pcl/point_types.h> #include <pcl/point_cloud.h> #include <pcl/search/kdtree.h> #include <pcl/io/pcd_io.h> #include <pcl/features/normal_3d_omp.h>//使用OMP需要添加的头文件 #include <pcl…

angular-引用本地json文件

angular-引用json文件&#xff0c;本地模拟数据时使用 在assets目录下存放json文件 大佬们的说法是&#xff1a;angular配置限定了资源文件的所在地&#xff08;就是assets的路径&#xff09;&#xff0c;放在其他文件夹中&#xff0c;angular在编译过程中会忽略&#xff0c;会…

jpg图片太大怎么压缩?3种压缩方法,一学就会

jpg图片太大怎么压缩&#xff1f;在日常生活和工作中&#xff0c;JPG图片过大不仅会导致存储空间的迅速消耗&#xff0c;还影响网络传输的速度&#xff0c;甚至在某些情况下&#xff0c;过大的图片文件还可能造成应用程序运行缓慢或崩溃&#xff0c;严重影响工作效率。因此&…

【Maven】介绍、下载及安装、集成IDEA

目录 一、什么是Maven Maven的作用 Maven模型 Maven仓库 二、下载及安装 三、IDEA集成Maven 1、POM配置详解 2、配置Maven环境 局部配置 全局设置 四、创建Maven项目 五、Maven坐标详解 六、导入Maven项目 方式1&#xff1a;使用Maven面板&#xff0c;快速导入项目 …

一周学会Django5 Python Web开发-Django5路由命名与反向解析reverse与resolve

锋哥原创的Python Web开发 Django5视频教程&#xff1a; 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计25条视频&#xff0c;包括&#xff1a;2024版 Django5 Python we…

【PyTorch][chapter 17][李宏毅深度学习]【无监督学习][ Auto-encoder]

前言&#xff1a; 本篇重点介绍AE&#xff08;Auto-Encoder&#xff09; 自编码器。这是深度学习的一个核心模型. 自编码网络是一种基于无监督学习方法的生成类模型,自编码最大特征输出等于输入 Yann LeCun&Bengio, Hinton 对无监督学习的看法. 目录&#xff1a; AE 模型原…

【C++】字符类型和字符数组-string

STL-容器 - string 字符串必须具备结尾字符\0 #include<iostream> #include<string> using namespace std; //STL-容器 - string char ch[101];//字符串必须具备结尾字符\0 int main() {int n; cin >> n;for (int i 0; i < n; i) {cin >> ch[i];}…

js如何抛异常,抛自定义的异常

js如何抛异常,抛自定义的异常 最简单的自定义异常 throw "hello" 来自chrome123的控制台的测试 throw "hello" VM209:1 Uncaught hello &#xff08;匿名&#xff09; VM209:1 try{ throw "hello";}catch(e){console.log(e);} VM338:1 hello…

每日coding 337打家劫舍III

337. 打家劫舍 III 小偷又发现了一个新的可行窃的地区。这个地区只有一个入口&#xff0c;我们称之为 root 。 除了 root 之外&#xff0c;每栋房子有且只有一个“父“房子与之相连。一番侦察之后&#xff0c;聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。…

08 按键消抖

在按键控制 LED中采用直接读取按键电平状态&#xff0c;然后根据电平状态控制LED。虽然直接读取按键电平状态然后执行相应处理程序的方法非常简单&#xff0c;但是这种方式可能存在误判问题&#xff0c;进而有可能导致程序功能异常&#xff0c;这是因为按键按下和松开时存在抖动…

WordPress后台自定义登录和管理页面插件Admin Customizer

WordPress默认的后台登录页面和管理员&#xff0c;很多站长都想去掉或修改一些自己不喜欢的功能&#xff0c;比如登录页和管理页的主题样式、后台左侧菜单栏的某些菜单、仪表盘的一些功能、后台页眉页脚某些小细节等等。这里boke112百科推荐这款可以让我们轻松自定义后台登录页…

定制学习风格、满足多元需求:Mr. Ranedeer 帮你打造 AI 家教 | 开源日报 No.178

JushBJJ/Mr.-Ranedeer-AI-Tutor Stars: 20.4k License: NOASSERTION Mr. Ranedeer 是一个个性化的 AI 辅导项目&#xff0c;主要功能包括使用 GPT-4 生成定制化提示&#xff0c;为用户提供个性化学习体验。其核心优势和特点包括&#xff1a; 调整知识深度以满足学习需求定制学…