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
執行輸出:
[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 = ?
執行輸出: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的所有可用優惠券
?
posted @ 2018-08-13 16:48 肖祥 閱讀(...) 評論(...) 編輯 收藏總結
以上是生活随笔為你收集整理的python 全栈开发,Day103(微信消息推送,结算中心业务流程)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 三相全控tc787触发电路_三相桥式全控
- 下一篇: Python调用Gurobi基本操作