路飞学城 之 基础配置
一、项目需求
1、五大需求如下
# 线上销售课程的 -商城 -知识付费类 # 需求 -首页功能 -轮播图接口 -推荐课程接口(没写) -用户功能 -用户名密码登录 -手机号验证码登录 -发送手机验证码 -验证手机号是否注册过 -注册接口 -课程列表功能 -课程列表接口 -排序,过滤,分页 -课程详情 -课程详情接口 -视频播放功能 -视频托管(第三方,自己平台) -下单功能 -支付宝支付:生成支付链接,付款,回调修改订单状态 -购买成功功能
2、环境配置
# 1 使用命令创建luffy项目 # 2 创建虚拟环境 mkvirtualenv -p python38 luffy # 3 安装django pip install django==3.1.12 # 3 命令创建项目 django-admin startproject luffy_api # 4 pycharm创建 luffy后台项目创建,目录调整 ├── luffy_api ├── logs/ # 项目运行时/开发时日志目录 - 包 ├── manage.py # 脚本文件 ├── luffy_api/ # 项目主应用,开发时的代码保存 - 包 ├── apps/ # 开发者的代码保存目录,以模块[子应用]为目录保存 - 包 ├── libs/ # 第三方类库的保存目录[第三方组件、模块] - 包 ├── settings/ # 配置目录 - 包 ├── dev.py # 项目开发时的本地配置 └── prod.py # 项目上线时的运行配置 ├── urls.py # 总路由 └── utils/ # 多个模块[子应用]的公共函数类库[自己开发的组件] └── scripts/ # 保存项目运营时的脚本文件 - 文件夹 # 1 运行报错 -django项目运行,要先加载settings.py(dev.py) -运行时,执行的是 python manage.py runserver # 2 解决运行报错 - 修改manage.py 中 os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffy_api.settings.dev') -命令行中运行,肯定不会报错 -pycharm运行,可能会报错 -删除之前的django-server,再创建一个,它会自动关联 # 3 创建app python manage.py startapp home # 在哪执行,app就创建在哪里 切到apps目录下,创建app即可 python ../../manage.py startapp home # 4 注册app -在INSTALLED_APPS 直接写app的名字,会报错,报模块找不到的错误---》 # No module named 'home' # 1 模块就是没有 # 2 不在环境变量中 # 3 自己写了一个,跟它同名 -只需要把apps路径加入到环境变量即可 sys.path.insert(0,str(BASE_DIR)) # 把apps文件夹加入环境变量,以后注册app,直接写名字即可 sys.path.insert(0, os.path.join(BASE_DIR, 'apps')) # 5 wsgi.py,asgi.py 配置文件也要改---》后面上线才用到 # 6 验证现在配置文件用的是dev.py(在dev文件中测试) # from django.conf import settings # print(settings) # luffy_api.settings.dev
补充:
pip freeze
# pip freeze > requirement.txt # cat requirement.txt asgiref==3.7.2 cffi==1.16.0 cryptography==41.0.4 Django==3.2.12 django-cors-headers==4.2.0 django-simpleui==2023.8.28 djangorestframework==3.14.0 Pillow==10.0.1 pycparser==2.21 PyMySQL==1.1.0 pytz==2023.3.post1 sqlparse==0.4.4 typing_extensions==4.8.0
二、
1、
# django 默认使用 python原生的日志模块 -以后不要再用print输出了,都用日志输出 -print输出,上线也会有输出,如果用日志,日志有级别,上线后把级别调高,你开发阶段的输出就不再打印了 # 可以使用第三 logru 公司里可能会用 # django中集成日志 -1 复制日志配置到dev.py中 LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'verbose': { 'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s' }, 'simple': { 'format': '%(levelname)s %(module)s %(lineno)d %(message)s' }, }, 'filters': { 'require_debug_true': { '()': 'django.utils.log.RequireDebugTrue', }, }, 'handlers': { 'console': { # 实际开发建议使用WARNING 'level': 'DEBUG', # 控制台只显示DEBUG以上,就是Info开始 'filters': ['require_debug_true'], 'class': 'logging.StreamHandler', 'formatter': 'simple' }, 'file': { # 实际开发建议使用ERROR 'level': 'INFO', #文件中只显示INFO以上,从WARNING 'class': 'logging.handlers.RotatingFileHandler', # 日志位置,日志文件名,日志保存目录必须手动创建,注:这里的文件路径要注意BASE_DIR代表的是小luffyapi 'filename': os.path.join(os.path.dirname(BASE_DIR), "logs", "luffy.log"), # 日志文件的最大值,这里我们设置300M 'maxBytes': 300 * 1024 * 1024, # 日志文件的数量,设置最大日志数量为10 'backupCount': 10, # 日志格式:详细格式 'formatter': 'verbose', # 文件内容编码 'encoding': 'utf-8' }, }, # 日志对象 'loggers': { 'django': { 'handlers': ['console', 'file'], 'propagate': True, # 是否让日志信息继续冒泡给其他的日志处理系统 }, } } -2 在utils下新建 common_logger.py import logging logger = logging.getLogger('django') -3 在想使用日志的位置,导入直接使用即可,日志有级别,控制台和文件中打印的日志级别是不一样的 from utils.common_logger import logger class LoggerView(APIView): def get(self, request): # 以后不要再用print输出了,都用日志输出 logger.info('info级别') logger.warn('warn级别') logger.warning('warning级别') logger.error('error级别') logger.critical('critical级别') logger.debug('debug级别') return Response('看到我了')
2、
##### 1 写一个函数(只要走到这,程序出异常了--》记录日志--》越详细越好) from rest_framework.views import exception_handler from rest_framework.response import Response from utils.common_logger import logger def common_exception_handler(exc, context): res = exception_handler(exc, context) if res: # 有值:drf的异常,处理了,格式不是咱们想要的 err = res.data.get('detail') or res.data or '未知错误,请联系系统管理员' response = Response({'code': 888, 'msg': '请求异常-drf:%s' % err}) else: # 其他异常,没有处理,自己处理格式 response = Response({'code': 999, 'msg': '请求异常-其他:%s' % str(exc)}) # 记录日志,越详细越好, 请求错误:请求地址是:%s,请求方式是:%s,请求用户ip地址是:%s,错误是:%s,执行的视图函数是:%s request = context.get('request') path = request.get_full_path() method = request.method ip = request.META.get('REMOTE_ADDR') user_id = request.user.pk or '未登录用户' err = str(exc) view = str(context.get('view')) logger.error( '请求错误:请求地址是:%s,请求方式是:%s,请求用户ip地址是:%s,用户id是:%s,错误是:%s,执行的视图函数是:%s' % ( path, method, ip, user_id, err, view)) return response ##### 2 在配置文件中配置一下 REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'utils.common_excepitons.common_exception_handler', }
补充:查找drf的配置,照着写
3、
# 之前使用drf的Response,我们需要自己构造返回字典 return Response(data={code:100,msg:成功,result:[{},{}]}) return Response(data={code:100,msg:成功,token:asdasd,username:lqz}) # 我们封装 APIResponse,以后使用,效果如下 return APIResponse()---》{code:100,msg:成功} return APIResponse(result=[{},{}]) return APIResponse(token=afasfd,username=lqz) ### 代码 from rest_framework.response import Response # APIResponse() # APIResponse(result=[{},{}]) # APIResponse(token=afasfd,username=lqz) # APIResponse(token=afasfd,username=lqz,status=201,headers={xx:'xx'}) class APIResponse(Response): def __init__(self, code=100, msg='成功', status=None, template_name=None, headers=None, exception=False, content_type=None, **kwargs): data = {'code': code, 'msg': msg} if kwargs: data.update(kwargs) super().__init__(data=data, status=status, headers=headers, template_name=template_name, exception=exception, content_type=content_type) # Response(data=data, status=status, headers=headers)
# 使用mysql作为数据库 -mysql在win上安装步骤:https://zhuanlan.zhihu.com/p/571585588 # 1 创建数据库--root用户创建---》后续使用代码操作数据库,不使用root用户,新建一个用户 luffy -如果使用root用户,一旦密码泄露,所有库都不安全了 -如果新建一个luffy用户,只授予luffy库的权限,即便泄露了密码,只是这个库不安全了 # 2 创建路飞用户,授予了luffy库所有表的所有权限 -查看用户:select user,host,authentication_string from mysql.user; -创建用户: grant all privileges on luffy.* to 'luffy'@'%' identified by 'Luffy123?'; grant all privileges on luffy.* to 'luffy'@'localhost' identified by 'Luffy123?'; flush privileges; mysql8: CREATE USER 'luffy'@'%' IDENTIFIED BY 'Luffy123?'; GRANT ALL PRIVILEGES ON luffy.* TO 'luffy'@'%'; # 3 以后,操作luffy库,都用luffy用户登录,就不用root用户了 # 4 项目中配置使用mysql数据库,使用luffy用户 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'luffy', 'HOST': '127.0.0.1', 'PORT': 3306, 'USER': 'luffy', 'PASSWORD': 'Luffy123?' } } # 5 运行会报错,因为没有装myslqclient -解决方式一:直接安装myslqclient ---》win平台看人品,mac基本装不上,linux需要单独处理 -解决方式二:使用pymysql -安装,在配置文件中加入: import pymysql pymysql.install_as_MySQLdb() # 6 数据库的用户名,密码,都是直接写死在代码中的,如果咱们代码被泄露了,数据库也就被人看到了 user=os.environ.get('LUFFY_USER','luffy') password=os.environ.get('LUFFY_PWD','Luffy123?') print(user) DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'luffy', 'HOST': '127.0.0.1', 'PORT': 3306, 'USER': user, 'PASSWORD': password } }
三、
elementui,全局配置使用
# 1 跟后端交互:axios cnpm install -S axios 以后想发送ajax请求,必须导入,使用 可以把axios放到vue实例中,以后任意组件中 this.$axios.get() main.js中加入 import axios from 'axios' Vue.prototype.$axios = axios; # 2 操作cookie: vue-cookies cnpm install -S vue-cookies import cookies from 'vue-cookies' Vue.prototype.$cookies = cookies; # 3 ui库:elementui cnpm install -S element-ui import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; // 全局都会有这个css的样式 Vue.use(ElementUI); # 4 去掉 标签的默认样式 1 编写全局css assets/css/global.css 2 main.js中导入 import '@/assets/css/global.css' # 5 全局配置文件 # 写一个settings.js export default { BASE_URL:'http://127.0.0.1:8000/' } # main.js中 import settings from "@/assets/js/settings"; Vue.prototype.$setting = settings
去除标签的默认样式
CSS Tools: Reset CSS (meyerweb.com)
2、三个组件
Banner.vue 轮播图组件
<template> <div class="banner"> <el-carousel height="400px"> <el-carousel-item v-for="item in banner_list" :key="item.id"> <div v-if="item.link.indexOf('http')==0"> <a :href="item.link"><img :src="item.image" alt=""></a> </div> <div v-else> <router-link :to="item.link"><img :src="item.image" alt=""></router-link> </div> </el-carousel-item> </el-carousel> </div> </template> <script> export default { name: "Banner", data() { return { banner_list: [] } }, created() { this.$axios.get(this.$settings.BASE_URL + 'home/banner/').then(res => { console.log(res.data) if (res.data.code == 100) { this.banner_list = res.data.result } else { this.$message({ showClose: true, message: res.data.msg, type: 'error' }); } }) } } var a = 'ss' var res = a.indexOf('ss') console.log(res) </script> <style scoped> .el-carousel__item { height: 400px; min-width: 1200px; } .el-carousel__item img { height: 400px; margin-left: calc(50% - 1920px / 2); } </style>
Header.vue 组件
<template> <div class="header"> <div class="slogan"> <p>老男孩IT教育 | 帮助有志向的年轻人通过努力学习获得体面的工作和生活</p> </div> <div class="nav"> <ul class="left-part"> <li class="logo"> <router-link to="/"> <img src="../assets/img/head-logo.svg" alt=""> </router-link> </li> <li class="ele"> <span @click="goPage('/free-course')" :class="{active: url_path === '/free-course'}">免费课</span> </li> <li class="ele"> <span @click="goPage('/actual-course')" :class="{active: url_path === '/actual-course'}">实战课</span> </li> <li class="ele"> <span @click="goPage('/light-course')" :class="{active: url_path === '/light-course'}">轻课</span> </li> </ul> <div class="right-part"> <div> <span>登录</span> <span class="line">|</span> <span>注册</span> </div> </div> </div> </div> </template> <script> export default { name: "Header", data() { return { url_path: sessionStorage.url_path || '/', } }, methods: { goPage(url_path) { // 已经是当前路由就没有必要重新跳转 if (this.url_path !== url_path) { this.$router.push(url_path); } sessionStorage.url_path = url_path; }, }, created() { sessionStorage.url_path = this.$route.path; this.url_path = this.$route.path; } } </script> <style scoped> .header { background-color: white; box-shadow: 0 0 5px 0 #aaa; /*属性用于在元素周围创建阴影效果*/ } .header:after { content: ""; display: block; clear: both; } .slogan { background-color: #eee; height: 40px; } .slogan p { width: 1200px; margin: 0 auto; color: #aaa; font-size: 13px; line-height: 40px; } .nav { background-color: white; user-select: none; width: 1200px; margin: 0 auto; } .nav ul { padding: 15px 0; float: left; } .nav ul:after { clear: both; content: ''; display: block; } .nav ul li { float: left; } .logo { margin-right: 20px; } .ele { margin: 0 20px; } .ele span { display: block; font: 15px/36px '微软雅黑'; border-bottom: 2px solid transparent; cursor: pointer; } .ele span:hover { border-bottom-color: orange; } .ele span.active { color: orange; border-bottom-color: orange; } .right-part { float: right; } .right-part .line { margin: 0 10px; } .right-part span { line-height: 68px; cursor: pointer; } </style>
Footer.vue 组件
<template> <div class="footer"> <ul> <li>关于我们</li> <li>联系我们</li> <li>商务合作</li> <li>帮助中心</li> <li>意见反馈</li> <li>新手指南</li> </ul> <p>Copyright © luffycity.com版权所有 | 京ICP备17072161号-1</p> </div> </template> <script> export default { name: "Footer" } </script> <style scoped> .footer { width: 100%; height: 128px; background: #25292e; color: #fff; } .footer ul { margin: 0 auto 16px; padding-top: 38px; width: 810px; } .footer ul li { float: left; width: 112px; margin: 0 10px; text-align: center; font-size: 14px; } .footer ul::after { content: ""; display: block; clear: both; } .footer p { text-align: center; font-size: 12px; } </style>
3、前端首页
轮播图,ElmentUI 叫走马灯 组件 | Element
推荐课程 :卡片 组件 | Element
<template> <div class="home"> <Header></Header> <Banner></Banner> <div> <el-row> <el-col :span="6" v-for="(o, index) in 8" :key="o" class="course_detail"> <el-card :body-style="{ padding: '0px' }"> <img src="http://photo.liuqingzheng.top/2023%2002%2022%2021%2057%2011%20/image-20230222215707795.png" class="image"> <div style="padding: 14px;"> <span>热门课程</span> <div class="bottom clearfix"> <time class="time">价格:199</time> <el-button type="text" class="button">查看详情</el-button> </div> </div> </el-card> </el-col> </el-row> </div> <img src="http://photo.liuqingzheng.top/2023%2003%2001%2016%2010%2034%20/1.png" alt="" width="100%" height="500px"> <Footer></Footer> </div> </template> <script> import Header from "@/components/Header.vue"; import Footer from "@/components/Footer.vue"; import Banner from "@/components/Banner.vue"; export default { name: 'HomeView', components: { Header, Footer, Banner } } </script> <style scoped> .time { font-size: 13px; color: #999; } .bottom { margin-top: 13px; line-height: 12px; } .button { padding: 0; float: right; } .image { width: 100%; display: block; } .clearfix:before, .clearfix:after { display: table; content: ""; } .clearfix:after { clear: both } .course_detail { padding: 50px; } </style>
四、
请求的url地址必须与浏览器上的url地址相同(包括端口、域名、协议等),否则会发生跨域问题
#1 前后端打通---出现CORS policy 错误 #2 同源策略 同源策略(Same origin policy)是一种约定,约定了请求的url地址,必须与浏览器上的url地址处于同域上,也就是域名,端口,协议相同,如果违背了这个约定,浏览器就会报错 请求正常发送成功,服务端也响应了,但是回来到浏览器的时候,报错,被浏览器的同源策略拦截了 #3 跨域问题出现的原因,源自于浏览器的同源策略 #4 只有前后端分离的web项目,才会出,才需要解决跨域问题 #5 CORS(跨域资源共享)---》是一个后端技术--》后端只需要在响应头中加入固定的响应头,前端就不禁止了 # 6 CORS请求分成两类(浏览器发送请求之前判断) -简单请求: 1 只发送一次请求,就是真正的请求 -非简单请求 1 先发送一个options 预检请求,服务端如果写了cors,再发送真正的请求,如果没有写cors,浏览就不再发送真正的请求了 # 7 只要同时满足以下两大条件,就属于简单请求。 # 1 请求方法是以下三种方法之一: HEAD GET POST #2 HTTP的头信息不超出以下几种字段: Accept Accept-Language Content-Language Last-Event-ID Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
from django.utils.deprecation import MiddlewareMixin class CORSMiddleWare(MiddlewareMixin): def process_response(self, request, response): # 简单请求 response['Access-Control-Allow-Origin'] = '*' # 允许所有客户端 # 非简单请求 if request.method == 'OPTIONS': # res['Access-Control-Allow-Methods'] = 'DELETE,PUT' response['Access-Control-Allow-Methods'] = '*' response['Access-Control-Allow-Headers'] = '*' return response
settings 引入
MIDDLEWARE = [ 'utils.common_middleware.CORSMiddleWare' ]
3、第三方
# 1 下载 pip install django-cors-headers # 2 注册app INSTALLED_APPS = ( ... 'corsheaders', ... ) # 3 加中间件 MIDDLEWARE = [ ... 'corsheaders.middleware.CorsMiddleware', ... ] # 4 settings配置文件 CORS_ORIGIN_ALLOW_ALL = True CORS_ALLOW_METHODS = ( 'DELETE', 'GET', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'VIEW', ) CORS_ALLOW_HEADERS = ( 'XMLHttpRequest', 'X_FILENAME', 'accept-encoding', 'authorization', 'content-type', 'dnt', 'origin', 'user-agent', 'x-csrftoken', 'x-requested-with', 'Pragma', 'token' )
五、
# 用户表,使用auth的user表,自己写 -如果用的是auth的user表,必须在迁移之前,就定好,扩写的要写完 # 咱们用户表,用的就是基于auth的user表,扩写,咱们先写用户表 1 创建用户app python ../../manage.py startapp user 2 创建用户表,models.py中 from django.contrib.auth.models import AbstractUser class User(AbstractUser): # 扩写,加入手机号,加入头像 mobile = models.CharField(max_length=11, unique=True) # 需要pillow包的支持 pip install pillow icon = models.ImageField(upload_to='icon', default='icon/default.png') class Meta: db_table = 'luffy_user' verbose_name = '用户表' verbose_name_plural = verbose_name def __str__(self): return self.username 3 dev.py中注册 # 注册user app INSTALLED_APPS = [ 'user', ] AUTH_USER_MODEL='user.User' # 应用名.表名 4 迁移命令 两条
# 1 瀑布开发模式 -架构,数据库设计---》分任务开发(周期很长,可能半年)---》测试---》上线 # 2 敏捷开发(devops) -架构设计--》很多板块 -开发某个版块,再设计相关板块的数据库 # 一周左右 -开发某个板块 -测试测试该板块 -运维上线该板块 -开发另一个版本
2、
from django.db import models class BaseModel(models.Model): # 是否显示,记录插入时间,最后修改时间,是否删除(软删除),排序 # 课程表,也需要这些字段 created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') updated_time = models.DateTimeField(auto_now=True, verbose_name='最后更新时间') is_delete = models.BooleanField(default=False, verbose_name='是否删除') is_show = models.BooleanField(default=True, verbose_name='是否上架') orders = models.IntegerField(verbose_name='优先级') class Meta: abstract = True # 这个表,只用来继承,不会再数据库生成表 from utils.common_model import BaseModel class Banner(BaseModel): title = models.CharField(max_length=16, unique=True, verbose_name='名称') image = models.ImageField(upload_to='banner', verbose_name='图片') # 地址,存放轮播图的地址 link = models.CharField(max_length=64, verbose_name='跳转链接') info = models.TextField(verbose_name='详情') # 也可以用详情表,宽高出处 class Meta: db_table = 'luffy_banner' verbose_name_plural = '轮播图表' def __str__(self): return self.title
补充:这里的is_delete字段,软删除
MySQL 数据库中,硬删除(也称为物理删除)指的是直接从数据库表中删除数据行,硬删除操作可能导致数据库索引失效
3、
APIResponse
from rest_framework.mixins import ListModelMixin from utils.common_response import APIResponse class CommonListModelMixin(ListModelMixin): def list(self, request, *args, **kwargs): res = super().list(request, *args, **kwargs) return APIResponse(result=res.data)
轮播图接口
from rest_framework.viewsets import ViewSetMixin, GenericViewSet from . import models from .serializer import BannerSerializer from django.conf import settings from utils.common_mixin import CommonListModelMixin class BannerView(GenericViewSet, CommonListModelMixin): # 自动生成路由 queryset = models.Banner.objects.filter(is_delete=False, is_show=True).order_by('orders')[:settings.BANNER_COUNT] serializer_class = BannerSerializer
4、序列化类
from rest_framework import serializers from .models import Banner class BannerSerializer(serializers.ModelSerializer): class Meta: model = Banner fields = ['id', 'image', 'link']
5、子路由
from django.contrib import admin from django.urls import path from home import views # pycharm报错,但实际上不报错 ,只需要把加入到环境变量的路径都做成source root即可 from rest_framework.routers import SimpleRouter from .views import BannerView router = SimpleRouter() # 127.0.0.1:8080/api/v1/home/banner/--- get router.register('banner', BannerView, 'banner') urlpatterns = [ ] urlpatterns += router.urls
6、配置文件
# dev配置 导入用户配置 from .user_settings import * ### user_settings.py # 用户自己配置,单独放到一个py文件中 BANNER_COUNT=3 # 后续可能还用别的配置
7、补充
# app 后端---》app一打开---》广告页---》可以滑动---》点击广告页---》一种浏览器打开页面跳转,一种情况打开一个他们app内的页面 # 原理就是轮播图接口
七、后台管理
# 任何项目,都会有后台管理 # 使用 django+vue ---》写---》 # django的admin做后台管理---》simpleui美化 # 1 下载simpleui---》app中注册 # 2 创建一个root用户,后台登录 # 3 后台登录,录入数据:admin中注册这个表 # 4 默认情况文件传到根路径下 需要配置:以后都会放到meida文件夹下 MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # 5 接口中不带meida 路径的: 配置文件中 MEDIA_URL = 'media/' # 取出的文件地址,拼接上media这个目录 # 6 前端能访问图片 path('media/<path:path>', serve, {'document_root': settings.MEDIA_ROOT}),
八、短信验证登录功能
1、
# 用户板块需要写的接口 1 用户名密码登录(多方式登录) 2 获取手机验证码接口 3 手机号+验证码登录 4 注册接口 5 验证手机号是否存在接口
2、验证手机号是否存在接口
user.view
from django.shortcuts import render # Create your views here. from django.utils.datastructures import MultiValueDictKeyError from rest_framework.viewsets import ViewSet from rest_framework.decorators import action from .models import User from utils.common_response import APIResponse class MobileView(ViewSet): # @action(methods=['GET'], detail=False) # def check_mobile(self, request, *args, **kwargs): # # 取出前端传入手机号 # mobile = request.query_params.get('mobile') # # 去数据中查询,是否存在即可 # user = User.objects.filter(mobile=mobile).first() # if user: # return APIResponse(msg='手机号存在') # else: # return APIResponse(code=101, msg='手机号不存在') @action(methods=['GET'], detail=False) def check_mobile(self, request, *args, **kwargs): try: # 取出前端传入手机号 mobile = request.query_params['mobile'] # 去数据中查询,是否存在即可 User.objects.get(mobile=mobile) # 有且只有一个才不报错,否则报错 except MultiValueDictKeyError as e: raise Exception('您没有携带手机号') # except ObjectDoesNotExis as e: # # raise Exception('手机号不存') except Exception as e: raise Exception('未知错误,请联系系统管理员') return APIResponse(msg='手机号存在')
开启子路由
from rest_framework.routers import SimpleRouter from .views import MobileView router = SimpleRouter() router.register('mobile', MobileView, 'mobile') urlpatterns = [ ] urlpatterns += router.urls
验证
127.0.0.1:8000/api/v1/user/mobile/check_mobile/?mobile=‘手机号’