昨日回顾
#1 Git的作用 1 对文件(代码)进行版本管理 2 完成 协同开发 项目,帮助程序员整合代码 i)帮助开发者合并开发的代码 ii)如果出现冲突代码的合并,会提示后提交合并代码的开发者,让其解决冲突 #2 Git简介 Git是分布式版本控制系统(在本地进行版本管理),控制的对象是开发的项目代码(文件) # 3 git和svn区别 # 4 Git,GitHub,GitLab,Gitee Git:是一种版本控制系统,是一个命令,是一种工具。 GitHub:是一个基于Git实现的在线代码托管仓库,包含一个网站界面,向互联网开放,公有仓库免费,部分私有仓库收费,全球最大的开源代码托管平台 GitLab:是一个基于Git实现的在线代码仓库托管软件,可以通过GitLab自己搭建一个类似于GitHub一样的系统,用在企业内部网络搭建Git私服,用于企业团队内部协作开发 Gitee:(码云) 是 OSCHINA 推出的代码托管平台,支持 Git 和 SVN,提供免费的私有仓库托管,面向互联网开发,分收费和付费,中国最大的开源代码托管平台 # 5 git 工作流程,如下图 # 6 常用命令 git add git commit -m # 7 扩展阅读 # git log 和git reflog的区别 git log 命令可以显示所有提交过的版本信息 如果感觉太繁琐,可以加上参数 --pretty=oneline,只会显示版本号和提交时的备注信息 git reflog 可以查看所有分支的所有操作记录(包括已经被删除的 commit 记录和 reset 的操作) # 8 git reset --hard,--mix,--soft的区别 hard (硬)-> 全部删除,会彻底返回到回退前的版本状态,了无痕迹 mixed (中)-> 保留工作目录,文件回退到未commit的状态 soft (软)-> 保留工作目录、暂存区 ,文件会回退到未 add(未到暂存)的状态 总结: soft是撤销commit的提交,但工作区未提交的更改还是保留; mixed是撤销暂存区的提交,工作区的更改同样也保留; 而hard是把工作区、暂存区、commit到仓库的三个版本都回滚了 # 9 Git忽略文件 .gitignore 直接写文件夹 直接写文件 *.mp4 !xx # 10 分支操作 #1.创建分支 git branch 分支名 #2.查看分支 git branch #3.切换分支 git checkout 分支名 # 4.创建并切换到分支 git checkout -b 分支名 # 5.删除分支 git branch -d 分支名 # 6.查看远程分支(列出所有分支,包含远程) git branch -a # 7.合并分支 git merge 分支名 把dev分支合并到master分支:切换到master分支,执行合并dev分支的命令 # 8.删除远程分支 git push origin --delete lqz # 9.新建远程分支 本地lqz分支建立完成 git push origin lqz # 11 dev和master分支合并 # 12 bug和master合并+dev和master合并---》可能会有冲突 # 13 远程仓库创建和提交代码 # 14 remote操作 # 1)查看仓库已配置的远程源 >: git remote >: git remote -v # 2)查看remote命令帮助文档 >: git remote -h # 3)删除远程源 >: git remote remove 源名 eg: git remote remove origin # 4)添加远程源 >: git remote add 源名 源地址 >: git remote add orgin https://gitee.com/liuqingzheng/app01.git # 5)提交代码到远程源 >: git push 源名 分支名 # 6)克隆远程源 >: git clone 远程源地址 # 7) 拉取代码 git pull origin master git fetch origin master # 拉取代码 # 15 ssh协议连接远程源 -加密几种方式: 1 编码:base64 urlencoded 2 摘要算法: md5,sha1 3 对称加密:aes 4 非对称加密:des -本地生成公钥私钥 -公钥配置在远程仓库 -以后就可以使用ssh的免密操作 # 16 协同开发 - 你是仓库创建者---》远程创建仓库为空 -本地有仓库 -本地没仓库 -你是仓库创建者---》远程创建仓库不为空 -本地仓库为空---》git clone下来,把代码复制到这个仓库中即可 -本地仓库不为空---》git clone下来--需要以远程仓库为准--》把代码复制到这个仓库中即可,不要复制.git文件夹 -你是开发者---》仓库创建者把你加为开发者--》你进你的账号---》就能看到这个项目 -git clone 下来,pycharm打开 -继续开发代码--》提交本地--》推到远程即可 -出现冲突就解决冲突即可 # 17 冲突解决 -多人在同一分支开发 -分支合并 -秘诀:少出冲突的原则是 不停的拉取代码 # 18 线上分支合并 提交pr:gitee 提交mr:gitlab # 19 为开源贡献代码 # 20 其他 变基操作:rebase 多个记录合成一个,提交记录更简洁 git pull 和git fetch 1. 相同点 首先在作用上他们的功能是大致相同的,都是起到了更新代码的作用。 2. 不同点 git pull 等同于 git fetch+git merge # 21 pycharm使用git # 21 gitlab 使用---》搭建是运维 -地址:http://192.168.1.252/ -超级管理员:root lqz123456 # 补充: 以后有前端项目 后端项目---》这俩项目,放一个仓库还是俩仓库? 每个项目一个仓库 之前每个项目都被git管理了, .git .gitignore project .git .gitignore front backend
今日内容
# 回退到某个版本 -git rest --hard 版本号 -推送到远端 git push origin master -f # 慎用 ,你不要用
# 用户板块---》原型图---分析需要写哪些接口 -多方式登录接口 -短信登录接口 -发送短信接口 -短信注册接口 -校验手机号是否注册接口
手机号是否存在
#### 视图类: class UserMobile(ViewSet): @action(methods=['post'], detail=False, url_path='check_mobile') def mobile(self, request): try: mobile = request.data.get('mobile') User.objects.get(mobile=mobile) # 能拿到说明手机号存在 return APIResponse(msg='手机号存在') except Exception as e: raise APIException(detail='手机号不存在') #### 路由 from .views import UserMobile from rest_framework.routers import SimpleRouter router = SimpleRouter() # /api/v1/user/mobile/check_mobile/ router.register('mobile', UserMobile, 'mobile') urlpatterns = [ ] ### 访问: url: http://127.0.0.1:8000/api/v1/user/mobile/check_mobile/ 请求方式:post请求 请求体: {"mobile":"18953575221"}
多方式登录接口
视图类
class UserLoginView(GenericViewSet): serializer_class = MulLoginSerializer # queryset = User.objects.all() # 不需要写 @action(methods=['POST'], detail=False) def mul_login(self, request): ser = self.get_serializer(data=request.data,context={'request':request}) ser.is_valid(raise_exception=True) token = ser.context.get('token') username = ser.context.get('username') icon = ser.context.get('icon') return APIResponse(token=token, username=username, icon=icon)
序列化类
from rest_framework import serializers from .models import User import re from rest_framework.exceptions import ValidationError, APIException from rest_framework_simplejwt.tokens import RefreshToken from django.conf import settings # 这个序列化类 只用来做 反序列化的校验,其他不用 class MulLoginSerializer(serializers.ModelSerializer): # 会自动把 models.py 中写的 username 和password 映射过来 # username = models.CharField( # max_length=150, # unique=True, # 唯一性校验,走到字段自己的校验规则,表示数据库中只能有一条---》会做校验---》去数据库查询有没有名字为lqz的记录-->数据库中有,字段自己规则就过不了了 # ) # 重写 username字段,去掉字段自己的规则 username = serializers.CharField() # password=serializers.CharField class Meta: model = User fields = ['username', 'password'] def validate(self, attrs): # 1 取出用户提交的用户名[用户名、手机、邮箱] 和密码 # 2 去数据库校验 user = self._get_user(attrs) # 3 校验通过,签发token token = self._get_token(user) # 4 放到context中 self._set_context(token, user) return attrs # 由于后续不用保存,这个可以不写 def _get_user(self, attrs): username = attrs.get('username') password = attrs.get('password') if re.match(r'^1[3-9][0-9]{9}$', username): user = User.objects.filter(mobile=username).first() elif re.match(r'^.+@.+$', username): # 邮箱登录 user = User.objects.filter(email=username).first() else: user = User.objects.filter(username=username).first() if user and user.check_password(password): return user else: raise APIException(detail='用户名或密码错误') def _get_token(self, user): refresh = RefreshToken.for_user(user) # self.context['refresh'] = str(refresh) return str(refresh.access_token) def _set_context(self, token, user): request = self.context.get('request') # print(request.headers.get('Host')) self.context['token'] = token self.context['username'] = user.username # icon 缺了前面 http://127.0.0.1:8000/media/ # 如果从request中取不出来服务端ip和端口 # 把ip和端口配置在配置文件中 # self.context['icon'] = request.META.get('HTTP_HOST')+'/media/'+str(user.icon) # user.icon 文件对象 self.context['icon'] = settings.BACKEND_URL + '/media/' + str(user.icon) # user.icon 文件对象
路由
from .views import UserMobile, UserLoginView from rest_framework.routers import SimpleRouter router = SimpleRouter() # /api/v1/user/mobile/check_mobile/ router.register('mobile', UserMobile, 'mobile') # /api/v1/user/login/mul_login/ router.register('login', UserLoginView, 'login') urlpatterns = [ ] urlpatterns += router.urls
腾讯云短信封装
# 项目中使用发送短信功能,借助于第三方 -腾讯云短信(咱们) -阿里 大于短信 -容联云短信 # 补充:短信轰炸 # 登录成功:https://console.cloud.tencent.com/smsv2 # 短信发送:https://cloud.tencent.com/document/product/382/55981 API:api接口,请求地址,携带参数,返回某些格式 -用起来比较麻烦 sdk:使用某种语言对api接口进行封装---》 -有sdk,优先用sdk,简单 -下载sdk,导入,掉方法执行,传入参数即可 # 下载sdk pip install tencentcloud-sdk-python
封装
# send_sms __init__.py settings.py sms.py
init.py
from .sms import get_code,common_send_sms
settings.py
SECRET_ID = '' SECRET_KEY = '' # 申请的短信应用 SDK AppID APP_ID = '' # 申请的短信模板ID,需要在短信控制台中申请 TEMPLATE_ID = '' # 申请的签名,参数使用的是`签名内容`,而不是`签名ID` SIGN = ""
sms.py
# 提供函数,给外部使用 from tencentcloud.common import credential from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException # 导入对应产品模块的client models。 from tencentcloud.sms.v20210111 import sms_client, models # 导入可选配置类 from tencentcloud.common.profile.client_profile import ClientProfile from tencentcloud.common.profile.http_profile import HttpProfile from . import settings # 1 生成随机数字验证码的函数 import random import json from rest_framework.exceptions import APIException def get_code(count=4): code = '' for i in range(count): code += str(random.randint(0, 9)) return code # 2 发送短信函数 def common_send_sms(code, mobile): try: cred = credential.Credential(settings.SECRET_ID, settings.SECRET_KEY) httpProfile = HttpProfile() httpProfile.reqMethod = "POST" # post请求(默认为post请求) httpProfile.reqTimeout = 30 # 请求超时时间,单位为秒(默认60秒) httpProfile.endpoint = "sms.tencentcloudapi.com" # 指定接入地域域名(默认就近接入) clientProfile = ClientProfile() clientProfile.signMethod = "TC3-HMAC-SHA256" # 指定签名算法 clientProfile.language = "en-US" clientProfile.httpProfile = httpProfile client = sms_client.SmsClient(cred, "ap-guangzhou", clientProfile) req = models.SendSmsRequest() req.SmsSdkAppId = settings.APP_ID # 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名 # 签名信息可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-sign) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-sign) 的签名管理查看 req.SignName = settings.SIGN # 模板 ID: 必须填写已审核通过的模板 ID # 模板 ID 可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-template) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-template) 的正文模板管理查看 req.TemplateId = settings.TEMPLATE_ID # 模板参数: 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致,,若无模板参数,则设置为空 req.TemplateParamSet = [code] # 下发手机号码,采用 E.164 标准,+[国家或地区码][手机号] # 示例如:+8613711112222, 其中前面有一个+号 ,86为国家码,13711112222为手机号,最多不要超过200个手机号 req.PhoneNumberSet = ["+86" + mobile] # 用户的 session 内容(无需要可忽略): 可以携带用户侧 ID 等上下文信息,server 会原样返回 req.SessionContext = "" # 短信码号扩展号(无需要可忽略): 默认未开通,如需开通请联系 [腾讯云短信小助手] req.ExtendCode = "" # 国内短信无需填写该项;国际/港澳台短信已申请独立 SenderId 需要填写该字段,默认使用公共 SenderId,无需填写该字段。注:月度使用量达到指定量级可申请独立 SenderId 使用,详情请联系 [腾讯云短信小助手](https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81)。 req.SenderId = "" resp = client.SendSms(req) # 输出json格式的字符串回包 resp = json.loads(resp.to_json_string(indent=2)) if resp.get('SendStatusSet')[0].get('Code') == 'Ok': return True else: return False except TencentCloudSDKException as err: raise APIException(detail=str(err)) except Exception as e: raise APIException(detail=str(e)) if __name__ == '__main__': print(get_code())
发送短信接口
# 发送短信接口 @action(methods=['get'], detail=False, url_path='send_sms') def send(self, request): try: mobile = request.query_params['mobile'] code = get_code() # 生成验证码,要存一下 之前验证码放session中,现在放在缓存中 cache.set('sms_code_%s' % mobile, code) # 放在缓存中,以手机号做区分 # res = common_send_sms(code, mobile) # 同步发送 # if res: # return APIResponse(msg='短信发送成功') # else: # return APIResponse(code=101, msg='短信发送失败') # 异步发送短信---不用管是否成功---》如果不成功,用户在发一次即可 t = Thread(target=common_send_sms, args=[code, mobile]) t.start() return APIResponse(msg='短信已发送') except MultiValueDictKeyError as e: raise APIException(detail='手机号必须携带') except Exception as e: # print(type(e)) raise APIException(detail=str(e))
短信登录功能
# 前端传入的数据 {mobile:1823433,code:8888}
视图类
class UserLoginView(GenericViewSet): @action(methods=['POST'], detail=False) def sms_login(self, request): return self._common_login(request) def get_serializer_class(self): if self.action == 'sms_login': return SmsLoginSerializer else: return super().get_serializer_class() def _common_login(self, request): ser = self.get_serializer(data=request.data, context={'request': request}) ser.is_valid(raise_exception=True) token = ser.context.get('token') username = ser.context.get('username') icon = ser.context.get('icon') return APIResponse(token=token, username=username, icon=icon)
序列化类
class CommonLoginSerializer(serializers.Serializer): def validate(self, attrs): user = self._get_user(attrs) token = self._get_token(user) self._set_context(token, user) return attrs def _get_user(self, attrs): raise Exception('这个方法必须被重写') def _get_token(self, user): refresh = RefreshToken.for_user(user) # self.context['refresh'] = str(refresh) return str(refresh.access_token) def _set_context(self, token, user): request = self.context.get('request') # print(request.headers.get('Host')) self.context['token'] = token self.context['username'] = user.username # icon 缺了前面 http://127.0.0.1:8000/media/ # 如果从request中取不出来服务端ip和端口 # 把ip和端口配置在配置文件中 # self.context['icon'] = request.META.get('HTTP_HOST')+'/media/'+str(user.icon) # user.icon 文件对象 self.context['icon'] = settings.BACKEND_URL + '/media/' + str(user.icon) # user.icon 文件对象 # 只做 反序列化的校验,其他不用 class SmsLoginSerializer(CommonLoginSerializer): mobile = serializers.CharField() code = serializers.CharField() def _get_user(self, attrs): # 1 取出验证码 code = attrs.get('code') mobile = attrs.get('mobile') old_code = cache.get('sms_code_%s' % mobile) # 2 校验验证码是否正确,不正确,抛异常 # 留了个后门,为了测试方便,不再真正发送验证码 if code == old_code or (settings.DEBUG and code == '8888'): # 3 拿着手机号查询用户,查不到用户,抛异常 user = User.objects.filter(mobile=mobile).first() if user: return user # 4 返回用户即可 else: raise APIException('用户不存在') else: raise APIException('验证码错误')
短信注册功能
# 前端传入的 {mobile,code,password}---->User表中字段:username必填,自动生成fake 我们让手机号做用户名
视图类
class UserRegister(GenericViewSet): serializer_class = RegisterSerializer # @action(methods=['POST'], detail=False) # def register(self, request): def create(self, request): ser = self.get_serializer(data=request.data) ser.is_valid(raise_exception=True) ser.save() # 走序列化类的create方法 return APIResponse(msg='恭喜您,注册成功')
序列化类
class RegisterSerializer(serializers.ModelSerializer): code=serializers.CharField() # 因为code不是表的字段,所以必须重写 class Meta: model = User fields = ['mobile', 'code', 'password'] def validate(self, attrs): # 1 校验验证码是否正确 code = attrs.pop('code') mobile = attrs.get('mobile') old_code = cache.get('sms_code_%s' % mobile) # 2 注册前的数据准备(用户名) if code == old_code or (settings.DEBUG and code == '6666'): # 2.1 code pop 出来 上面做了 # 2.2 把用户名放入 attrs['username'] = mobile else: raise APIException('验证码错误') # 3 返回校验过后的数据 return attrs def create(self, validated_data): # validated_data {mobile,password,username} user = User.objects.create_user(**validated_data) return user
路由
#/api/v1/user/register/ router.register('register', UserRegister, 'register')
标签:git,06days,get,++,self,mobile,code,user,luffy From: https://www.cnblogs.com/wzh366/p/17985677