昨日回顾
#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')