python 全栈开发,Day103(微信消息推送,结算中心业务流程)
python 全栈开发,Day103(微信消息推送,结算中心业务流程)
昨日内容回顾
第一部分:考试题(Python基础)第二部分:路飞相关 1. 是否遇到bug?难解决的技术点?印象深刻的事?- orm操作费劲- 最开始学习路由系统时候,匹配规则;答案一:有,但主要不是在技术上而是在业务上;在支付时:贝里、支付宝、满减、立减、折扣;答案二:编写API时,如果继承ModelViewSet相关的类之后,必须在静态字段中写:querysetclass AuthView(ModelViewSet):queryset = models.xxx.all()否则,在渲染器渲染好看的页面时,会报错。解决方案:a. 不继承,继承APIViewb. 定义渲染器class AuthView(GenaricViewSet):render_classes = [JSONrender,]queryset = models.xxx.all()答案三:在刚学习时候,xxxxxxx大忌:非常简单的功能 2. 路飞学城项目架构是怎么样?- 管理后台- 导师后台- 主站 - 前端:1人 - 后端:3人(1+0.5+1+0.5+0.5)- UI:1人 3. 路飞学城中你负责写过什么?API:- 第一类:基本增删改查- 课程列表- 学位课- 专题课- 课程详细- 课程大纲 - 价格策略- 推荐课程- 课程章节 - 用户评价- 常见问题- 文章列表- 文章详细- 评论- 点赞- 收藏- 学习中心- 我的账户- 我的订单- 作业- 回答- 提问...- 第二类:支付流程- 购物车- 加入购物车- 查看 - 修改价格策略- 删除购物车中的课程 4. 路飞学城购物车的结构?
View Code
一、微信消息推送
面对大量的垃圾信息拟门会报告垃圾短信,导致了大部分消息推送方式的失效或者效果打折,今天谈的是效果非常好的微信通知服务
因为这里推送的都是有效信息,屏蔽的人非常少,微信不允许你把它当做推广工具,所以都有调用次数等规则限制,但我们可以好好利用以保持应用的用户粘性。
公众号
公众号分别3种:订阅号,服务号,企业号。
其中订阅号,分为未认证和已认证。已认证需要公司营业执照相关信息,服务号和企业号,也需要公司相关信息。
基于沙箱环境做消息推送
由于用户体验和安全性方面的考虑,微信公众号的注册有一定门槛,某些高级接口的权限需要微信认证后才可以获取。
所以,为了帮助开发者快速了解和上手微信公众号开发,熟悉各个接口的调用,我们推出了微信公众帐号测试号,通过手机微信扫描二维码即可获得测试号。
进入微信公众帐号测试号申请系统
单击登录,扫描一下二维码
打开手机微信,确认登录
进入之后,就会有一个测试账号
接下来的代码,需要使用appID和appsecret
网页的表单,一律不需要填写!!!
找到下面的测试好二维码,将图片下载下来
直接右键-->另存为即可
项目测试
新建一个项目wxbox,注意:django版本为1.11
修改urls.py
from django.conf.urls import url from django.contrib import adminfrom app01 import views urlpatterns = [url(r'^admin/', admin.site.urls),url(r'^index/', views.index), ]
View Code
将templates目录剪切到app01目录下,在app01目录下,创建static。为什么这么做呢?
因为这样的话,每一个应用都有独立的templates和static。
那么django能找到它吗?
默认是从项目根目录查找,如果找不到,会从应用名目录中取找。找不到,就报错!
将下载的图片放到static中,新建index.html
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <h1>欢迎使用:路飞学城</h1><h3>扫描关注路飞学诚(用于消息提示,请必须关注)</h3> <img style="width: 200px;" src="/static/0.jpg" alt=""></body> </html>
View Code
修改views.py
from django.shortcuts import render,HttpResponse# Create your views here. def index(request):"""首页:param request::return:"""return render(request,"index.html")
View Code
启动django项目,访问首页
获取用户的微信id
使用另外一个微信号,扫描一下,点击关注
这里会出现一个微信号
注意:这里的微信号,并不是真正的微信号,它是一段随机码。它只是和这个公众号做了一下绑定,为了唯一标识这个用户!
假设这个用户,由关注了别的公众号。那么会出现一个新的随机码!
也就是说:不同公众号,同一个人,是不一样的!
脚本测试
使用前,安装模块
pip3 install requests
获取token
因为要向用户发送消息,所以需要使用token认证,才能发送!
在项目根目录创建 test.py
注意:修改里面的appid和secret,改为自己的
#!/usr/bin/env python # -*- coding: utf-8 -*- import json import requests# 1. 伪造浏览器向 https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential... 发送GET请求,并获取token r1 = requests.get(url="https://api.weixin.qq.com/cgi-bin/token",params={"grant_type": "client_credential","appid": 'wx038ba9d6c87fxxxx',"secret": 'fd1ebdff49010aa67a1457bbe250xxxx',} )print(r1.text) # 获取文本 print('*'*30) print(r1.json()) # 反序列化 print('*'*30)access_token = r1.json().get('access_token') # 获取token print(access_token)
View Code
执行输出:
{"access_token":"12_23d1iIREKvhS0N6EislyKdaV9MhcaXgBAgTnH_5ivrobK4Ek6uoD9_35nvlL8SO76dGQ-T6wnzmVaXi3wHhCigaw85JqMV47DM0rq664ZEYdfOWypvyplByJ6yts-F3RYthMkWtfG-bGyyzDYZVfADAGHD","expires_in":7200} ****************************** {'expires_in': 7200, 'access_token': '12_23d1iIREKvhS0N6EislyKdaV9MhcaXgBAgTnH_5ivrobK4Ek6uoD9_35nvlL8SO76dGQ-T6wnzmVaXi3wHhCigaw85JqMV47DM0rq664ZEYdfOWypvyplByJ6yts-F3RYthMkWtfG-bGyyzDYZVfADAGHD'} ****************************** 12_23d1iIREKvhS0N6EislyKdaV9MhcaXgBAgTnH_5ivrobK4Ek6uoD9_35nvlL8SO76dGQ-T6wnzmVaXi3wHhCigaw85JqMV47DM0rq664ZEYdfOWypvyplByJ6yts-F3RYthMkWtfG-bGyyzDYZVfADAGHD
View Code
给指定用户发送消息
复制这里面的微信号,它可以定位到这个用户
修改test.py,指定微信号
#!/usr/bin/env python # -*- coding: utf-8 -*- import json import requests# 1. 伪造浏览器向 https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential... 发送GET请求,并获取token r1 = requests.get(url="https://api.weixin.qq.com/cgi-bin/token",params={"grant_type": "client_credential","appid": 'wx038ba9d6c87fxxxx',"secret": 'fd1ebdff49010aa67a1457bbe250dxxx',} )# print(r1.text) # 获取文本 # print('*'*30) # print(r1.json()) # 反序列化 # print('*'*30) access_token = r1.json().get('access_token') # 获取token # print(access_token)# 2. 给指定用户发送普通消息消息:access_token/ wx_id = 'oe0j758m-Zp4QBOvgzF8vGBCOxxx' # 微信号 body = {"touser": wx_id,"msgtype": "text", # 本文"text": {"content": 'hello'} }r2 = requests.post(url="https://api.weixin.qq.com/cgi-bin/message/custom/send",params={'access_token': access_token},# 发送的消息,必须是bytes类型data=bytes(json.dumps(body,ensure_ascii=False),encoding='utf-8') )print(r2.text)
View Code
解释:request,内部发送的数据是字节。默认格式为Latin-1,它不支持中文!
所以需要使用json序列化。如果发送的是字符串,它会使用Latin-1转码为bytes,遇到中文会报错
所以我这里,直接强转为bytes,那么它内部就不会转码了,直接发送!
执行脚本,输出:
{"errcode":0,"errmsg":"ok"}
看状态,发送成功。
查看用户接收的消息
发送模板消息
发送模板消息,脚本需要指定新的url
https://api.weixin.qq.com/cgi-bin/message/template/send
默认这里是空的,点击新建测试模板
注意:参数需以{{开头,以.DATA}}结尾。模板标题不能使用参数
这里的user,就是用户的微信名
接下发送时,需要这个模板id
修改test.py
#!/usr/bin/env python # -*- coding: utf-8 -*- import json import requests# 1. 伪造浏览器向 https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential... 发送GET请求,并获取token r1 = requests.get(url="https://api.weixin.qq.com/cgi-bin/token",params={"grant_type": "client_credential","appid": 'wx038ba9d6c87fxxxx',"secret": 'fd1ebdff49010aa67a1457bbe250xxx',} )access_token = r1.json().get('access_token') # 获取token # print(access_token)# 2. 给指定用户发送普通消息消息:access_token/ wx_id = 'oe0j758m-Zp4QBOvgzF8vGBCOxxx' # 微信号 body = {"touser": wx_id,"template_id": 'OxCBr-98eVMkTLKb0ZGpLGK5mNnLMLoEZG2MsmR3Q_Q', # 模板id"data": {"user": {"value": "小明同学","color": "#173177"}} }r2 = requests.post(url="https://api.weixin.qq.com/cgi-bin/message/template/send",params={'access_token': access_token},data=json.dumps(body) )print(r2.text)
View Code
执行脚本,输出:
{"errcode":0,"errmsg":"ok","msgid":412032057779486721}
查看手机消息
推送消息,一天可以发送10万次
这里演示的向用户发送推送消息,必须让用户关注公众号。从公众号后台的用户列表中,得到用户的微信号(随机字符串)
才可以准确发送到用户手机上,只有管理者,才能看到这个微信号
自动获取用户-消息发送
上面的脚本,演示的是手动发送。如果用户过多,发送消息会很繁琐!
如何让用户做简单的操作,就能自动发送消息给用户呢?
路飞学城购买课程之后,不能直接观看视频。要填写报名表才能观看!
填写相关信息后,发送微信消息
这里只做简单的功能:让用户扫描二维码,将微信号自动存储在数据库中,并发送消息
创建用户表
修改models.py
from django.db import modelsclass UserInfo(models.Model):name = models.CharField(max_length=32,verbose_name="用户名")pwd = models.CharField(max_length=64,verbose_name="密码")wx_id = models.CharField(max_length=32,null=True,blank=True,verbose_name="微信号")
View Code
执行2个命令,生成表
python manage.py makemigrations python manage.py migrate
使用navicat打开sqlite3数据库
添加一条记录,微信号保持为空
用户登录
修改urls.py,增加路径
urlpatterns = [url(r'^admin/', admin.site.urls),url(r'^index/', views.index),url(r'^login/', views.login), ]
View Code
新建login.html
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <form method="post">{% csrf_token%}<input type="text" name="user"><input type="text" name="pwd"><input type="submit" value="提交">{{ msg }} </form> </body> </html>
View Code
修改views.py
from django.shortcuts import render,HttpResponse,redirect from app01 import models# Create your views here. def login(request):"""用户登陆:param request::return:"""if request.method == 'GET':return render(request, 'login.html')user = request.POST.get('user')pwd = request.POST.get('pwd')obj = models.UserInfo.objects.filter(name=user, pwd=pwd).first()if obj:request.session['user_info'] = {'id': obj.id, 'name': obj.name}return redirect('/index/')return render(request, 'login.html', {'msg': '用户名或密码错误'})def index(request):"""首页:param request::return:"""return render(request,"index.html")
View Code
修改index.html,显示登录用户名
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <h1>欢迎 {{ request.session.user_info.name }} 使用:路飞学城</h1><h3>扫描关注路飞学诚(用于消息提示,请必须关注)</h3> <img style="width: 200px;" src="/static/0.jpg" alt=""></body> </html>
View Code
访问登录页面
登录之后,效果如下:
下一步,需要获取用户的微信号
怎么获取呢?让用户访问一个链接,执行上面的脚本,得到用户的微信号,并存入数据库!
js生成二维码
jquery.qrcode.js 是一个能够在客户端生成矩阵二维码QRCode 的jquery插件 ,使用它可以很方便的在页面上生成二维条码。
如何使用它
将jquery.qrcode.min.js和jquery添加到您的网页中
<script src="jquery.min.js"></script> <script type="text/javascript" src="jquery.qrcode.min.js"></script>
然后创建一个DOM元素去包含生成qr码。
<div id="qrcode"></div>
最后你在此容器中的添加qrcode
<script>jQuery(function(){jQuery('#qrcode').qrcode("http://www.jq22.com"); }) </script>
就这么简单,您想要的二维码就生成了。
在static目录中创建js目录,将3个文件jquery.min.js,jquery.qrcode.min.js,qrcode.js放到此目录
修改index.html,生成二维码
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <h1>欢迎 {{ request.session.user_info.name }} 使用:路飞学城</h1><h3>扫描关注路飞学诚(用于消息提示,请必须关注)</h3> <img style="width: 200px;" src="/static/0.jpg" alt=""><script src="/static/js/jquery.min.js"></script> <script src="/static/js/jquery.qrcode.min.js"></script> <script src="/static/js/qrcode.js"></script> <script>$(function () {// empty()表示清空// text表示内容$('#qrcode').empty().qrcode({text: 'http://www.py3study.com/'});}) </script></body> </html>
View Code
刷新index页面,效果如下:
用户扫描最下面的二维码,就会自动打开网页: http://www.py3study.com/
获取微信号并写入数据库
获取微信号,需要在这里配置回调的url,点击修改
通过该接口,可以获取用户的基本信息(获取用户的OpenID是无需用户同意的,获取用户的基本信息则需用户同意)
这里必须公网IP才行
注意:冒号不能是中文!!!否则报url不匹配
线上部署
登录到公网服务器,将项目拷贝过去
注意:python版本为3.5,django版本为1.11
安装requests
pip3 install requests
修改urls.py,增加路径
urlpatterns = [url(r'^admin/', admin.site.urls),url(r'^index/', views.index),url(r'^login/', views.login),url(r'^get_qrcode/', views.get_qrcode),url(r'^get_wx_id/', views.get_wx_id), ]
View Code
修改settings.py,运行所有IP访问
ALLOWED_HOSTS = ['*',]
修改views.py,增加视图函数
注意:修改appid,secret改成自己的。redirect_uri改成自己的公网IP和端口
from django.shortcuts import render, redirect from app01 import models from django.http import JsonResponse, HttpResponse# Create your views here. def login(request):"""用户登陆:param request::return:"""if request.method == 'GET':return render(request, 'login.html')user = request.POST.get('user')pwd = request.POST.get('pwd')obj = models.UserInfo.objects.filter(name=user, pwd=pwd).first()if obj:request.session['user_info'] = {'id': obj.id, 'name': obj.name}return redirect('/index/')return render(request, 'login.html', {'msg': '用户名或密码错误'})def index(request):"""首页:param request::return:"""return render(request, "index.html")def get_qrcode(request):"""访问获取用户基本信息接口需要接收3个参数:param request::return:"""ret = {'status': True, 'data': None}# appid 是自己的appid# redirect_uri 是回调地址,腾讯服务器会访问它# state是自定义的,变量名可以随意access_url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid={appid}&redirect_uri={redirect_uri}&response_type=code&scope=snsapi_userinfo&state={state}#wechat_redirect"url = access_url.format(appid='wx038ba9d6c87fxxxx',redirect_uri="http://45.xx.192.14:8000/get_wx_id/", # 跳转会我的网站state=request.session['user_info']['id'] # 用户ID )ret['data'] = url # 生成urlreturn JsonResponse(ret) # 返回给ajaxdef get_wx_id(request):"""获取微信ID,并更新到数据库:param request::return:"""import requestscode = request.GET.get("code")state = request.GET.get("state")# 获取该用户openId(用户唯一,用于给用户发送消息)r1 = requests.get(url="https://api.weixin.qq.com/sns/oauth2/access_token",params={"appid": 'wx038ba9d6c87fxxxx',"secret": 'fd1ebdff49010aa67a1457bbe250xxxx',"code": code,"grant_type": 'authorization_code',}).json()# 获取的到openid表示用户授权成功wx_id = r1.get("openid")user = models.UserInfo.objects.filter(id=state).first()# 判断用户id不为空if not user.wx_id:user.wx_id = wx_iduser.save() # 修改微信idreturn HttpResponse('授权成功')
View Code
修改index.html,增加ajax
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <h1>欢迎 {{ request.session.user_info.name }} 使用:路飞学城</h1><h3>扫描关注路飞学诚(用于消息提示,请必须关注)</h3> <img style="width: 250px;" src="/static/0.jpg" alt=""><h3>下一步:请将微信ID发给我</h3> <div id="qrcode" style="width: 250px;height: 250px;background-color: white;"></div><script src="/static/js/jquery.min.js"></script> <script src="/static/js/jquery.qrcode.min.js"></script> <script src="/static/js/qrcode.js"></script> <script>$(function () {get_qcode();});function get_qcode() {$.ajax({url: '/get_qrcode/',type: 'GET',dataType: 'JSON',success: function (arg) {//arg.data 就是访问获取用户基本信息接口的url地址// 需要接收3个参数$('#qrcode').empty().qrcode({text: arg.data});console.log(arg.data);}})} </script></body> </html>
View Code
访问公网的登录地址
登录成功后,进入首页
可以发现最下面的二维码,更加复杂了。因为里面加了很长的url
查看console
使用手机扫描二维码,它其实就是访问上面很长的url
点击允许
点击继续访问
提示授权成功
查看服务器的终端输出
它先访问的是get_qrcode
get_wx_id是腾讯访问的
将sqlite3下载下来,使用navicat查看数据库
发现微信id更新了
网页点击发送消息
单独做一个页面,点击指定的用户,发送消息
修改urls.py,增加路径
urlpatterns = [url(r'^admin/', admin.site.urls),url(r'^index/', views.index),url(r'^login/', views.login),url(r'^get_qrcode/', views.get_qrcode),url(r'^get_wx_id/', views.get_wx_id),url(r'^send/', views.send),url(r'^send_msg/', views.send_msg), ]
View Code
修改views.py
from django.shortcuts import render,redirect from app01 import models from django.http import JsonResponse, HttpResponse# Create your views here. def login(request):"""用户登陆:param request::return:"""if request.method == 'GET':return render(request, 'login.html')user = request.POST.get('user')pwd = request.POST.get('pwd')obj = models.UserInfo.objects.filter(name=user, pwd=pwd).first()if obj:request.session['user_info'] = {'id': obj.id, 'name': obj.name}return redirect('/index/')return render(request, 'login.html', {'msg': '用户名或密码错误'})def index(request):"""首页:param request::return:"""return render(request,"index.html")def get_qrcode(request):"""访问获取用户基本信息接口需要接收3个参数:param request::return:"""ret = {'status': True, 'data': None}# appid 是自己的appid# redirect_uri 是回调地址,腾讯服务器会访问它# state是自定义的,变量名可以随意access_url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid={appid}&redirect_uri={redirect_uri}&response_type=code&scope=snsapi_userinfo&state={state}#wechat_redirect"url = access_url.format(appid='wx038ba9d6c87fxxxx',redirect_uri="http://45.xx.192.14:8000/get_wx_id/", # 跳转会我的网站state=request.session['user_info']['id'] # 用户ID )ret['data'] = url # 生成urlreturn JsonResponse(ret) # 返回给ajaxdef get_wx_id(request):"""获取微信ID,并更新到数据库:param request::return:"""import requestscode = request.GET.get("code")state = request.GET.get("state")# 获取该用户openId(用户唯一,用于给用户发送消息)r1 = requests.get(url="https://api.weixin.qq.com/sns/oauth2/access_token",params={"appid": 'wx038ba9d6c87fxxxx',"secret": 'fd1ebdff49010aa67a1457bbe250xxxx',"code": code,"grant_type": 'authorization_code',}).json()# 获取的到openid表示用户授权成功wx_id = r1.get("openid")user = models.UserInfo.objects.filter(id=state).first()# 判断用户id不为空if not user.wx_id:user.wx_id = wx_iduser.save() # 修改微信idreturn HttpResponse('授权成功')def send(request):"""发送消息:param request::return:"""user_list = models.UserInfo.objects.all()return render(request, 'send.html', {'user_list': user_list})def send_msg(request):"""发送消息:param request::return:"""id = request.session['user_info']['id']obj = models.UserInfo.objects.filter(id=id).first()import jsonimport requests# 1. 伪造浏览器向 https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential... 发送GET请求,并获取tokenr1 = requests.get(url="https://api.weixin.qq.com/cgi-bin/token",params={"grant_type": "client_credential","appid": 'wx038ba9d6c87fxxxx',"secret": 'fd1ebdff49010aa67a1457bbe250xxxx',})access_token = r1.json().get('access_token')body = {"touser": obj.wx_id,"template_id": 'OxCBr-98eVMkTLKb0ZGpLGK5mNnLMLoEZG2MsmR3Q_Q',"data": {"user": {"value": "Hello Kitty","color": "#173177"}}}r2 = requests.post(url="https://api.weixin.qq.com/cgi-bin/message/template/send",params={'access_token': access_token},data=json.dumps(body))print(r2.text)return HttpResponse('发送成功')
View Code
创建send.html
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body><ul>{% for item in user_list %}<li>{{ item.name }} {{ item.wx_id }} <a href="/send_msg/">发送消息</a></li>{% endfor %}</ul> </body> </html>
View Code
访问页面,点击发送消息
提示发送成功
查看手机信息
流程图
解释:
1. 浏览器访问服务器的登录页面
2. 服务器返回登录成功,跳转首页。并生成底部的二维码,链接就是获取个人信息的
3. 用户扫描二维码,关注公众号
4. 引导用户进入授权页面同意授权,用户扫描下面的二维码
5. 用户手机访问链接,用来获取微信个人信息的
6. 微信服务器获取个人信息,访问回调地址redirect_uri,也就是个人服务器。
7. 个人服务器访问微信服务器,通过code换取网页授权access_token(与基础支持中的access_token不同)
8. 微信服务器将个人信息返回给服务器,服务器更新数据库
总结:
a.路飞中使用的是已认证的服务号b.基于微信ID发送消息- 获取AccessToken- 发送消息:- AccessToken- 微信ID- 消息内容注意:AccessToken的超时时间8小时c.诱导用户发送微信ID"间接"给我步骤: 1. 申请沙箱环境账号2. 手动发送消息3. 写django程序: - 创建用户表:wx_id- 登陆页面- "填写报名报" 扫码- 公众号- 授权码,基于qrcode.js库完成+ajax从后台获取的要跳转的地址(腾讯)- 公众号后台设置:网页授权获取用户基本信息 --> xx.xx.xx.xx:8000- redirect_uri指定要要跳转的地址4. - 回调函数,从腾讯中获取用户微信ID并更新到自己数据库- 获取微信ID,并更新到数据库- 获取该用户openId- 获取的到openid表示用户授权成功- 更新数据库 5.页面发送- 用户列表- 要发送的内容- 按钮:发送- 1. 伪造浏览器向公众号发送GET请求,并获取token- 2. 给指定用户发送普通消息消息- 3. 给指定用户发送模板消息
View Code
将appid,secretredirect_uri 移植到settings.py中
完整代码请参考github
https://github.com/987334176/WeChat
二、结算中心业务流程
购物车3个状态:放入购物车,结算,支付
结算页面
进入结算页面,购物车不会清空。只有支付成功后,才会删除购物车的商品!
结算中心和购物车一样,是一个中间状态,可以删除/修改价格策略
它比购物车,多了一个字段-->优惠券
优惠券这里面涉及的逻辑,就比较复杂了
由于时间关系,没法一一细说。
直接上表结构
下载最后一个版本的代码
https://github.com/987334176/luffycity/archive/v1.5.zip
修改models.py,增加3个表。注意:course要开启优惠券
完整代码如下:
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation from django.contrib.contenttypes.models import ContentType from django.db.models import Q from django.utils.safestring import mark_safe from django.db import models import hashlib# ######################## 课程相关 ########################class CourseCategory(models.Model):"""课程大类, e.g 前端 后端..."""name = models.CharField(max_length=64, unique=True)def __str__(self):return "%s" % self.nameclass Meta:verbose_name_plural = "01.课程大类"class CourseSubCategory(models.Model):"""课程子类, e.g python linux """category = models.ForeignKey("CourseCategory")name = models.CharField(max_length=64, unique=True)def __str__(self):return "%s" % self.nameclass Meta:verbose_name_plural = "02.课程子类"class DegreeCourse(models.Model):"""学位课程"""name = models.CharField(max_length=128, unique=True)course_img = models.CharField(max_length=255, verbose_name="缩略图")brief = models.TextField(verbose_name="学位课程简介", )total_scholarship = models.PositiveIntegerField(verbose_name="总奖学金(贝里)", default=40000) # 2000 2000mentor_compensation_bonus = models.PositiveIntegerField(verbose_name="本课程的导师辅导费用(贝里)", default=15000)period = models.PositiveIntegerField(verbose_name="建议学习周期(days)", default=150) # 为了计算学位奖学金prerequisite = models.TextField(verbose_name="课程先修要求", max_length=1024)teachers = models.ManyToManyField("Teacher", verbose_name="课程讲师")# 用于GenericForeignKey反向查询, 不会生成表字段,切勿删除# coupon = GenericRelation("Coupon")# 用于GenericForeignKey反向查询,不会生成表字段,切勿删除degreecourse_price_policy = GenericRelation("PricePolicy")def __str__(self):return self.nameclass Meta:verbose_name_plural = "03.学位课"class Teacher(models.Model):"""讲师、导师表"""name = models.CharField(max_length=32)role_choices = ((0, '讲师'), (1, '导师'))role = models.SmallIntegerField(choices=role_choices, default=0)title = models.CharField(max_length=64, verbose_name="职位、职称")signature = models.CharField(max_length=255, help_text="导师签名", blank=True, null=True)image = models.CharField(max_length=128)brief = models.TextField(max_length=1024)def __str__(self):return self.nameclass Meta:verbose_name_plural = "04.导师或讲师"class Scholarship(models.Model):"""学位课程奖学金"""degree_course = models.ForeignKey("DegreeCourse")time_percent = models.PositiveSmallIntegerField(verbose_name="奖励档位(时间百分比)", help_text="只填百分值,如80,代表80%")value = models.PositiveIntegerField(verbose_name="奖学金数额")def __str__(self):return "%s:%s" % (self.degree_course, self.value)class Meta:verbose_name_plural = "05.学位课奖学金"class Course(models.Model):"""专题课/学位课模块表"""name = models.CharField(max_length=128, unique=True)course_img = models.CharField(max_length=255)sub_category = models.ForeignKey("CourseSubCategory")course_type_choices = ((0, '付费'), (1, 'VIP专享'), (2, '学位课程'))course_type = models.SmallIntegerField(choices=course_type_choices)# 不为空;学位课的某个模块# 为空;专题课degree_course = models.ForeignKey("DegreeCourse", blank=True, null=True, help_text="若是学位课程,此处关联学位表")brief = models.TextField(verbose_name="课程概述", max_length=2048)level_choices = ((0, '初级'), (1, '中级'), (2, '高级'))level = models.SmallIntegerField(choices=level_choices, default=1)pub_date = models.DateField(verbose_name="发布日期", blank=True, null=True)period = models.PositiveIntegerField(verbose_name="建议学习周期(days)", default=7) # order = models.IntegerField("课程顺序", help_text="从上一个课程数字往后排")attachment_path = models.CharField(max_length=128, verbose_name="课件路径", blank=True, null=True)status_choices = ((0, '上线'), (1, '下线'), (2, '预上线'))status = models.SmallIntegerField(choices=status_choices, default=0)template_id = models.SmallIntegerField("前端模板id", default=1)coupon = GenericRelation("Coupon")# 用于GenericForeignKey反向查询,不会生成表字段,切勿删除price_policy = GenericRelation("PricePolicy")asked_question = GenericRelation("OftenAskedQuestion")def __str__(self):return "%s(%s)" % (self.name, self.get_course_type_display())def save(self, *args, **kwargs):if self.course_type == 2:if not self.degree_course:raise ValueError("学位课程必须关联对应的学位表")super(Course, self).save(*args, **kwargs)class Meta:verbose_name_plural = "06.专题课或学位课模块"class CourseDetail(models.Model):"""课程详情页内容"""course = models.OneToOneField("Course")hours = models.IntegerField("课时")course_slogan = models.CharField(max_length=125, blank=True, null=True)video_brief_link = models.CharField(verbose_name='课程介绍', max_length=255, blank=True, null=True)why_study = models.TextField(verbose_name="为什么学习这门课程")what_to_study_brief = models.TextField(verbose_name="我将学到哪些内容")career_improvement = models.TextField(verbose_name="此项目如何有助于我的职业生涯")prerequisite = models.TextField(verbose_name="课程先修要求", max_length=1024)recommend_courses = models.ManyToManyField("Course", related_name="recommend_by", blank=True)teachers = models.ManyToManyField("Teacher", verbose_name="课程讲师")def __str__(self):return "%s" % self.courseclass Meta:verbose_name_plural = "07.课程或学位模块详细"class OftenAskedQuestion(models.Model):"""常见问题"""content_type = models.ForeignKey(ContentType) # 关联course or degree_courseobject_id = models.PositiveIntegerField()content_object = GenericForeignKey('content_type', 'object_id')question = models.CharField(max_length=255)answer = models.TextField(max_length=1024)def __str__(self):return "%s-%s" % (self.content_object, self.question)class Meta:unique_together = ('content_type', 'object_id', 'question')verbose_name_plural = "08. 常见问题"class CourseOutline(models.Model):"""课程大纲"""course_detail = models.ForeignKey("CourseDetail")title = models.CharField(max_length=128)# 前端显示顺序order = models.PositiveSmallIntegerField(default=1)content = models.TextField("内容", max_length=2048)def __str__(self):return "%s" % self.titleclass Meta:unique_together = ('course_detail', 'title')verbose_name_plural = "09. 课程大纲"class CourseChapter(models.Model):"""课程章节"""course = models.ForeignKey("Course")chapter = models.SmallIntegerField(verbose_name="第几章", default=1)name = models.CharField(max_length=128)summary = models.TextField(verbose_name="章节介绍", blank=True, null=True)pub_date = models.DateField(verbose_name="发布日期", auto_now_add=True)class Meta:unique_together = ("course", 'chapter')verbose_name_plural = "10. 课程章节"def __str__(self):return "%s:(第%s章)%s" % (self.course, self.chapter, self.name)class CourseSection(models.Model):"""课时目录"""chapter = models.ForeignKey("CourseChapter")name = models.CharField(max_length=128)order = models.PositiveSmallIntegerField(verbose_name="课时排序", help_text="建议每个课时之间空1至2个值,以备后续插入课时")section_type_choices = ((0, '文档'), (1, '练习'), (2, '视频'))section_type = models.SmallIntegerField(default=2, choices=section_type_choices)section_link = models.CharField(max_length=255, blank=True, null=True, help_text="若是video,填vid,若是文档,填link")video_time = models.CharField(verbose_name="视频时长", blank=True, null=True, max_length=32) # 仅在前端展示使用pub_date = models.DateTimeField(verbose_name="发布时间", auto_now_add=True)free_trail = models.BooleanField("是否可试看", default=False)class Meta:unique_together = ('chapter', 'section_link')verbose_name_plural = "11. 课时"def __str__(self):return "%s-%s" % (self.chapter, self.name)class Homework(models.Model):chapter = models.ForeignKey("CourseChapter")title = models.CharField(max_length=128, verbose_name="作业题目")order = models.PositiveSmallIntegerField("作业顺序", help_text="同一课程的每个作业之前的order值间隔1-2个数")homework_type_choices = ((0, '作业'), (1, '模块通关考核'))homework_type = models.SmallIntegerField(choices=homework_type_choices, default=0)requirement = models.TextField(max_length=1024, verbose_name="作业需求")threshold = models.TextField(max_length=1024, verbose_name="踩分点")recommend_period = models.PositiveSmallIntegerField("推荐完成周期(天)", default=7)scholarship_value = models.PositiveSmallIntegerField("为该作业分配的奖学金(贝里)")note = models.TextField(blank=True, null=True)enabled = models.BooleanField(default=True, help_text="本作业如果后期不需要了,不想让学员看到,可以设置为False")class Meta:unique_together = ("chapter", "title")verbose_name_plural = "12. 章节作业"def __str__(self):return "%s - %s" % (self.chapter, self.title)# class CourseReview(models.Model): # """课程评价""" # enrolled_course = models.OneToOneField("EnrolledCourse") # about_teacher = models.FloatField(default=0, verbose_name="讲师讲解是否清晰") # about_video = models.FloatField(default=0, verbose_name="内容实用") # about_course = models.FloatField(default=0, verbose_name="课程内容通俗易懂") # review = models.TextField(max_length=1024, verbose_name="评价") # disagree_number = models.IntegerField(default=0, verbose_name="踩") # agree_number = models.IntegerField(default=0, verbose_name="赞同数") # tags = models.ManyToManyField("Tags", blank=True, verbose_name="标签") # date = models.DateTimeField(auto_now_add=True, verbose_name="评价日期") # is_recommend = models.BooleanField("热评推荐", default=False) # hide = models.BooleanField("不在前端页面显示此条评价", default=False) # # def __str__(self): # return "%s-%s" % (self.enrolled_course.course, self.review) # # class Meta: # verbose_name_plural = "13. 课程评价(购买课程后才能评价)" # # # class DegreeCourseReview(models.Model): # """学位课程评价 # 为了以后可以定制单独的评价内容,所以不与普通课程的评价混在一起,单独建表 # """ # enrolled_course = models.ForeignKey("EnrolledDegreeCourse") # course = models.ForeignKey("Course", verbose_name="评价学位模块", blank=True, null=True, # help_text="不填写即代表评价整个学位课程", limit_choices_to={'course_type': 2}) # about_teacher = models.FloatField(default=0, verbose_name="讲师讲解是否清晰") # about_video = models.FloatField(default=0, verbose_name="视频质量") # about_course = models.FloatField(default=0, verbose_name="课程") # review = models.TextField(max_length=1024, verbose_name="评价") # disagree_number = models.IntegerField(default=0, verbose_name="踩") # agree_number = models.IntegerField(default=0, verbose_name="赞同数") # tags = models.ManyToManyField("Tags", blank=True, verbose_name="标签") # date = models.DateTimeField(auto_now_add=True, verbose_name="评价日期") # is_recommend = models.BooleanField("热评推荐", default=False) # hide = models.BooleanField("不在前端页面显示此条评价", default=False) # # def __str__(self): # return "%s-%s" % (self.enrolled_course, self.review) # # class Meta: # verbose_name_plural = "14. 学位课评价(购买课程后才能评价)"class PricePolicy(models.Model):"""价格与有课程效期表"""content_type = models.ForeignKey(ContentType) # 关联course or degree_courseobject_id = models.PositiveIntegerField()content_object = GenericForeignKey('content_type', 'object_id')# course = models.ForeignKey("Course")valid_period_choices = ((1, '1天'), (3, '3天'),(7, '1周'), (14, '2周'),(30, '1个月'),(60, '2个月'),(90, '3个月'),(180, '6个月'), (210, '12个月'),(540, '18个月'), (720, '24个月'),)valid_period = models.SmallIntegerField(choices=valid_period_choices)price = models.FloatField()class Meta:unique_together = ("content_type", 'object_id', "valid_period")verbose_name_plural = "15. 价格策略"def __str__(self):return "%s(%s)%s" % (self.content_object, self.get_valid_period_display(), self.price)# ################################### 优惠券相关 #################################class Coupon(models.Model):"""优惠券生成规则"""name = models.CharField(max_length=64, verbose_name="活动名称")brief = models.TextField(blank=True, null=True, verbose_name="优惠券介绍")coupon_type_choices = ((0, '立减'), (1, '满减券'), (2, '折扣券'))coupon_type = models.SmallIntegerField(choices=coupon_type_choices, default=0, verbose_name="券类型")money_equivalent_value = models.IntegerField(verbose_name="等值货币")off_percent = models.PositiveSmallIntegerField("折扣百分比", help_text="只针对折扣券,例7.9折,写79", blank=True, null=True)minimum_consume = models.PositiveIntegerField("最低消费", default=0, help_text="仅在满减券时填写此字段")content_type = models.ForeignKey(ContentType, blank=True, null=True)object_id = models.PositiveIntegerField("绑定课程", blank=True, null=True, help_text="可以把优惠券跟课程绑定")content_object = GenericForeignKey('content_type', 'object_id')quantity = models.PositiveIntegerField("数量(张)", default=1)open_date = models.DateField("优惠券领取开始时间")close_date = models.DateField("优惠券领取结束时间")valid_begin_date = models.DateField(verbose_name="有效期开始时间", blank=True, null=True)valid_end_date = models.DateField(verbose_name="有效结束时间", blank=True, null=True)# coupon_valid_days = models.PositiveIntegerField(verbose_name="优惠券有效期(天)", blank=True, null=True,# help_text="自券被领时开始算起")date = models.DateTimeField(auto_now_add=True)class Meta:verbose_name_plural = "31. 优惠券生成记录"def __str__(self):return "%s(%s)" % (self.get_coupon_type_display(), self.name)class CouponRecord(models.Model):"""优惠券发放、消费纪录"""coupon = models.ForeignKey("Coupon")account = models.ForeignKey("Account", verbose_name="拥有者")number = models.CharField(max_length=64, unique=True)status_choices = ((0, '未使用'), (1, '已使用'), (2, '已过期'))status = models.SmallIntegerField(choices=status_choices, default=0)get_time = models.DateTimeField(verbose_name="领取时间", help_text="用户领取时间")used_time = models.DateTimeField(blank=True, null=True, verbose_name="使用时间")# order = models.ForeignKey("Order", blank=True, null=True, verbose_name="关联订单") # 一个订单可以有多个优惠券order_id = models.IntegerField(verbose_name='关联订单ID')class Meta:verbose_name_plural = "32. 用户优惠券"def __str__(self):return '%s-%s-%s' % (self.account, self.number, self.status)class Account(models.Model):username = models.CharField("用户名", max_length=64, unique=True)email = models.EmailField(verbose_name='邮箱',max_length=255,unique=True,blank=True,null=True)password = models.CharField('密码', max_length=128)class Meta:verbose_name_plural = "33. 用户表"
View Code
执行2个命令,生成表
python manage.py makemigrations python manage.py migrate
录入几条数据
作业
考试题(Python基础)
1. 写代码实现:val = "i am a string",实现⼀个⽅法,将字符串逆序输出(2分)
val = "i am a string" def func(st):return st[::-1]print(func(val))
View Code
执行输出: gnirts a ma i
2. 判断101-200之间有多少个质数(2分)
提示:⼀个⼤于1的⾃然数,除了1和它⾃身外,不能被其他⾃然数整除的书叫质数 循环加判断,取余数为0
L1 = []for x in range(101, 200):n = 0for y in range(1, x + 1):if x % y == 0:n = n + 1if n == 2:# print(x) L1.append(x)print(L1)
View Code
执行输出:
[101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199]
View Code
3. 简述正则表达式中的贪婪匹配并举例说明(2分)
贪婪匹配:正则表达式一般趋向于最大长度匹配,也就是所谓的贪婪匹配。如下面使用模式p匹配字符串str,结果就是匹配到:abcaxc(ab*c)。 如:String str="abcaxc";Patter p="ab*c";
View Code
4. 写代码(3分)
v1 = {11,22,33}
v2 = {22,44,55}
a. 如何获取 v1 中存在⽽v2中不存在的值?
b. 如何获取 v2 中存在⽽v2中不存在的值?
c. 如何获取v1和v2中都存在的值?
print(v1-v2) # 差集 print(v2-v1) # 差集 print(v1.intersection(v2)) # 交集
View Code
5. 请编写⼀个函数实现将IP地址转换成⼀个整数(4分)
如 10.3.9.12 转换规则为: 10 00001010 3 00000011 9 00001001 12 00001100 再将以上⼆进制拼接起来计算⼗进制结果,即: 00001010 00000011 00001001 00001100 = ?
def func(args):st = ''for i in args:new_i = bin(i) # 将数字转化为二进制new_i = new_i[2:] # 将转化后的二进制去除'0b'前缀new_i = new_i[2:].rjust(8, '0') # 二进制前面填充0,固定总长度8st += new_i[2:].rjust(8, '0') # 将四个数字的二进制进行拼接st = int(st, base=2) # 拼接后的字符串转化为10进制数字return stli = [10, 3, 9, 12] print(func(li))
View Code
执行输出:33554688
ORM查询
获取用户ID=1的所有优惠券
obj = models.Account.objects.filter(id=1).first()for i in obj.couponrecord_set.all():print(i.coupon.id,i.coupon.get_coupon_type_display(),i.coupon.off_percent)
View Code
获取专题课ID=1且用户ID=10的所有优惠券
obj1 = models.Course.objects.filter(id=1).first()print(obj1.coupon.all())for i in obj1.coupon.all():print(i)
View Code
获取用户ID=10的所有未绑定课程的优惠券
obj2 = models.CouponRecord.objects.filter(account=1,coupon__object_id__isnull=False)for i in obj2:print(i.coupon.id,i.coupon.get_coupon_type_display(),i.coupon.off_percent)
View Code
获取用户ID=1的所有可用优惠券
obj3 = models.CouponRecord.objects.filter(status=0,account_id=1)for i in obj3:print(i.coupon.id,i.coupon.get_coupon_type_display(),i.coupon.off_percent)
View Code
python 全栈开发,Day103(微信消息推送,结算中心业务流程)相关推荐
- python 全栈开发,Day104(DRF用户认证,结算中心,django-redis)
python 全栈开发,Day104(DRF用户认证,结算中心,django-redis) 考试第二部分:MySQL数据库 6. MySQL中char和varchar的区别(1分) char是定长, ...
- python费用结算系统_python 全栈开发,Day104(DRF用户认证,结算中心,django-redis)
考试第二部分:MySQL数据库 6. MySQL中char和varchar的区别(1分) char是定长,varchar是变长. char的查询速度比varchar要快. View Code 7. ...
- python全栈开发工程师_【2018重磅巨献】老男孩Python高级全栈开发工程师第三期...
2018年老男孩python全栈第三期老男孩python全栈3期 老男孩教育Python全栈开发为国内首家专注python全方面开发的教育机构 老男孩Python全栈第三期课程详细目录,高清不加密! ...
- python 全栈开发,Day125(HTML5+ 初识,HBuilder,夜神模拟器,Webview)
python 全栈开发,Day125(HTML5+ 初识,HBuilder,夜神模拟器,Webview) 昨日内容回顾 1.增删改查:增:db.collections.insert({a:1}) // ...
- Java后台微信点餐小程序开发最新版笔记,Springboot+Mysql+Freemarker+Bootstrap+微信小程序实现扫码点餐小程序,包含语音提示,微信消息推送,网页管理后台
由于之前的Java后台微信点餐小程序有些知识点过时了,所以今天重新出一版,把里面过时的知识点更新下 前五章是部署笔记,后面是知识点的详细讲解,大家可以先看部署笔记,部署起来后,再跟着详细知识学习. 第 ...
- python 全栈开发,Day32(知识回顾,网络编程基础)
python 全栈开发,Day32(知识回顾,网络编程基础) 一.知识回顾 正则模块 正则表达式 元字符 :. 匹配除了回车以外的所有字符\w 数字字母下划线\d 数字\n \s \t 回车 空格 和 ...
- 收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!
Python Web全栈开发入门实战教程教程 大家好,我叫亓官劼(qí guān jié ),这个<Python Web全栈开发入门实战教程教程>是一个零基础的实战教程,手把手带你开 ...
- python全栈开发中级班全程笔记(第二模块、第四章)(常用模块导入)
python全栈开发笔记第二模块 第四章 :常用模块(第二部分) 一.os 模块的 详解 1.os.getcwd() :得到当前工作目录,即当前python解释器所在目录路径 impor ...
- 学python有前途吗-Python全栈开发有前景吗,是否值得学习?
谢邀 一.什么是Python web全栈工程师? 全栈工程师是指掌握多种技能,并能利用多种技能独立完成产品的人.也叫全端工程师(同时具备前端和后台能力),英文Full Stack developer. ...
最新文章
- 快手基于 RocketMQ 的在线消息系统建设实践
- WebSphere MQ For AIX和HP-UX安装配置
- 【Android 插件化】VAHunt 检测插件化引擎的具体细节
- 改工作空间_打拼六年换的新房,装修花了17万,飘窗改柜子很实用,谁见过?...
- .Net Core 中的包、元包与框架(Packages, Metapackages and Frameworks)
- 我的ASP.NET AJAX控件——PopupNotificationExtender:实现OWA或Messenger样式的信息提示窗口...
- c# 读hex_在C#中将string转换为hexstring
- 机器学习算法 --- SVM (Support Vector Machine)
- pgsql merge方法
- python解析配置文件
- FPGA实现伪彩色图像
- 车牌识别算法实践(二):基础算法
- 【详细】endnote中英文文献混排
- light动名词_动名词的用法英语语法大全
- 初识F5之BIGIP-LTM负载均衡
- 转换工具推荐:如何将PDF文档转换为PPT演示文稿
- Titanic 泰坦尼克号预测-Tensorflow 方法-【Kaggle 比赛】
- 重庆邮电大学计算机2019湖北分数线,重庆邮电大学2019年各省各批次录取分数线...
- 游戏直播平台新赛程:负重前行与危中求生
- 使用ArrayList集合实现学生管理系统
热门文章
- CentOS 7中使用Subversion Edge安装Subversion服务器
- 动态规划: dp+递推——确定动态矩阵dp含义,确定每个状态下面临的选择和对结果值影响,选择符合题意的作为结果存储在dp中
- C# 调用百度api接口获取节假日
- c语言中式等号右边赋予左边吗,二项式定理公式、各种例题讲解及练习
- 中科大 南大计算机专业哪个好,南京大学与中国科学技术大学的计算机专业哪个好?...
- WORD的三种选定文本的方法
- ERP和SAP之间的关系
- 短视频配音用什么软件好?短视频配音软件免费哪款好用?
- stm32通过I2C实现温湿度(AHT20)采集
- 每天学一点flash(5) actionscript 3.0 外部加载mp3音乐