作為一枚中臺(tái)測(cè)試侨把,我們的功能太過(guò)依賴(lài)上游數(shù)據(jù)犀变,比如我只需要測(cè)試支付,但必須要一步步填寫(xiě)各種資料提交訂單才可以測(cè)支付功能秋柄,此類(lèi)數(shù)據(jù)我們可以通過(guò)python的request获枝、json模塊模擬創(chuàng)建訂單過(guò)程,實(shí)現(xiàn)一秒創(chuàng)建訂單骇笔。
梳理完成該功能步驟:
1.學(xué)習(xí)了解request省店、json模塊嚣崭,抓取請(qǐng)求,封裝為函數(shù)懦傍,參數(shù)處理雹舀、發(fā)送請(qǐng)求、接收處理返回結(jié)果
2.Django多數(shù)據(jù)聯(lián)用粗俱,復(fù)雜功能依賴(lài)多個(gè)數(shù)據(jù)庫(kù)
4.頁(yè)面模擬請(qǐng)求说榆、校驗(yàn)返回結(jié)果、返回關(guān)鍵信息到頁(yè)面
一寸认、學(xué)習(xí)了解request签财、json模塊
1.1 抓取請(qǐng)求
使用charles或者fiddler抓取創(chuàng)建訂單請(qǐng)求,保存請(qǐng)求偏塞、參數(shù)列表唱蒸、返回結(jié)果
1.2 處理參數(shù)列表為python字典格式
使用vim替換功能格式化列表為python字典的格式
1.3 封裝為函數(shù),模擬發(fā)送請(qǐng)求灸叼、接收響應(yīng)結(jié)果
使用request模擬客戶端提交訂單請(qǐng)求神汹,請(qǐng)求參數(shù)、接收返回結(jié)果并轉(zhuǎn)換為json格式返回
代碼示例:
# coding:utf-8
import requests
import json
'''
author:xxx
功能:
1.創(chuàng)建xx訂單
古今。屁魏。。
創(chuàng)建日期:xxxx-xx-xx
修改日期:xxxx-xx-xx by xxx
'''
# 提交xx訂單
def commitOrder(userId, xxId, providerId, productId, compactId, token):
url = 'http://mobile-api.xxx.com/xxxapi/xx_xx?ck=16840f92-1f83-4a68-8a39-e6262f6f11e5'
data = {
......(非關(guān)鍵參數(shù)直接給定義值即可)
'xxx': str(xxId),
......
'xxx': str(ProductId),
'xxx': str(userid),
......
'xxx': str(providerId),
......
'xxx': str(compactId),
......
'xxx': str(userid),
......
'_t': token,
......
'xxx': '[{"id":"0","xxx":"autoCreateOrder"}]'
}
res_1 = requests.post(url, data).json()
res_2 = json.dumps(res_1, indent=2)
res = json.loads(res_2)
content = res.get('content')
return content
二沧卢、Django多數(shù)據(jù)聯(lián)用
該功能參考了尚學(xué)堂以為老師的攻略蚁堤,整個(gè)分享寫(xiě)的非常非常詳細(xì):
https://code.ziqiangxuetang.com/django/django-tutorial.html
2.1在項(xiàng)目中創(chuàng)建database_router.py添加如下代碼
# -*- coding: utf-8 -*-
from django.conf import settings
DATABASE_MAPPING = settings.DATABASE_APPS_MAPPING
class DatabaseAppsRouter(object):
"""
A router to control all database operations on models for different
databases.
In case an app is not set in settings.DATABASE_APPS_MAPPING, the router will fallback to the `default` database.
Settings example:
DATABASE_APPS_MAPPING = {'app1': 'db1', 'app2': 'db2'}
"""
def db_for_read(self, model, **hints):
""""Point all read operations to the specific database."""
if model._meta.app_label in DATABASE_MAPPING:
return DATABASE_MAPPING[model._meta.app_label]
return None
def db_for_write(self, model, **hints):
"""Point all write operations to the specific database."""
if model._meta.app_label in DATABASE_MAPPING:
return DATABASE_MAPPING[model._meta.app_label]
return None
def allow_relation(self, obj1, obj2, **hints):
"""Allow any relation between apps that use the same database."""
db_obj1 = DATABASE_MAPPING.get(obj1._meta.app_label)
db_obj2 = DATABASE_MAPPING.get(obj2._meta.app_label)
if db_obj1 and db_obj2:
if db_obj1 == db_obj2:
return True
else:
return False
return None
# for Django 1.4 - Django 1.6
def allow_syncdb(self, db, model):
"""Make sure that apps only appear in the related database."""
if db in DATABASE_MAPPING.values():
return DATABASE_MAPPING.get(model._meta.app_label) == db
elif model._meta.app_label in DATABASE_MAPPING:
return False
return None
# Django 1.7 - Django 1.11
def allow_migrate(self, db, app_label, model_name=None, **hints):
print(db, app_label, model_name, hints)
if db in DATABASE_MAPPING.values():
return DATABASE_MAPPING.get(app_label) == db
elif app_label in DATABASE_MAPPING:
return False
return None
2.2 在setting.py中添加app、數(shù)據(jù)庫(kù)但狭、app-db對(duì)應(yīng)關(guān)系
INSTALLED_APPS添加應(yīng)用:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'user',
'account',
'contract',
'order',
......
]
DATABASES添加數(shù)據(jù)庫(kù):
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'DBname1',
'USER': 'username',
'PASSWORD': 'pwd',
'HOST': 'ip地址',
'PORT': '3306',
},
'users': {
'ENGINE':'django.db.backends.mysql',
'NAME': 'DBname2',
'HOST': 'ip地址',
'USER': 'username',
'PASSWORD': 'pwd',
'PORT': '3306'
},
'contract': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'DBname3',
'HOST': 'ip地址',
'USER': 'username',
'PASSWORD': 'pwd',
'PORT': '3306'
},
'order': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'DBname4',
'HOST': 'ip地址',
'USER': 'username',
'PASSWORD': 'pwd',
'PORT': '3306'
},
'account': {
'ENGINE':'django.db.backends.mysql',
'NAME': 'DBname4',
'HOST': 'ip地址',
'USER': 'username',
'PASSWORD': 'pwd',
'PORT': '3306'
},
......
}
2.3 定義常量
DATABASE_ROUTERS = ['tools.database_router.DatabaseAppsRouter']
2.4定義DATABASE_APPS_MAPPING:
DATABASE_APPS_MAPPING = {
#'app_name':'database_name',
'user': 'users',
'contract':'contract',
'order':'order',
'account':'account',
'xxapp':'xxDB',
......
}
2.5 在應(yīng)用模型類(lèi)中添加如下代碼:
app_label = 'users'
2.6 操作數(shù)據(jù)庫(kù)代碼:
userId = User.objects.using('users').get(username=userName).fld_userid
三披诗、頁(yè)面設(shè)計(jì)
左側(cè)點(diǎn)擊創(chuàng)建訂單,右側(cè)顯示創(chuàng)建訂單頁(yè)面
創(chuàng)建訂單頁(yè)面需要用戶名立磁、服務(wù)提供者信息呈队、服務(wù)類(lèi)型下拉列表(bootstrap下拉列表控件)等信息
{% load i18n %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>bootstrap 搜索下拉框</title>
<!-- jquery -->
<script src="http://cdn.staticfile.org/jquery/2.1.1/jquery.min.js" type="text/javascript"></script>
<!-- bootstrap -->
<link rel="stylesheet">
<script src="http://cdn.staticfile.org/twitter-bootstrap/3.3.1/js/bootstrap.min.js" type="text/javascript"></script>
<!-- bootstrap-select -->
<link rel="stylesheet" >
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.12.4/js/bootstrap-select.min.js"></script>
<style>
table {
width: 60%;
border: 0px;
{#mso-cellspacing:1px ;#}
cellpadding:"4";
background-color: azure;
align-content: center;
}
.title{
width: 25%;
}
{#td{#}
{# width: 100%;#}
{#}#}
input{
width: 100%;
background-color: azure;
}
select,option{
width: 300px;
}
</style>
</head>
<body>
<h1>創(chuàng)建訂單頁(yè)面</h1>
<form method="POST" action="createorder_action">
<table border="1" >
<tr>
<td class="title">用戶名</td>
<td><input type="text" name="userName" placeholder="請(qǐng)輸入xx用戶名"></td>
</tr>
<tr>
<td class="title">新建xx名稱(chēng)</td>
<td><input type="text" name="patientName" placeholder="新建xx名"></td>
</tr>
<tr>
<td class="title">服務(wù)提供者用戶名</td>
<td><input type="text" name="docotorName" placeholder="請(qǐng)輸入服務(wù)提供者用戶名"></td>
</tr>
<tr>
<td class="title">服務(wù)類(lèi)型</td>
<td>
<select name='servicetype' class="selectpicker show-tick form-control" data-live-search="true" width="50%">
<option value="1">服務(wù)1(fuwu1)</option>
<option value="2">服務(wù)2(fuwu2)</option>
<option value="3">服務(wù)4(fuwu3)</option>
<option value="4">服務(wù)5(fuwu4)</option>
<option value="5">服務(wù)5(fuwu5)</option>
<option value="6">服務(wù)6(fuwu6)</option>
</select>
</td>
</tr>
<tr>
<td>1分錢(qián)訂單(僅支持xx服務(wù)功能)</td>
<td>是<input type="radio" name="is_one_money" value="1" style="width:30px" />
否<input type="radio" name="is_one_money" value="0" checked="checked" style="width:30px"/>
</td>
</tr>
<tr>
<td colspan="2" align="center"><button type="submit">創(chuàng)建訂單</button></td>
<td></td>
</tr>
</table>
</form>
<div><h2>{{ message }}</h2></div>
</body>
</html>
四、處理函數(shù)
涉及公司內(nèi)部信息較多唱歧,截取部分代碼
view.py編寫(xiě)處理函數(shù)
def createorder(request):
response = render(request, "createorder.html")
return response
def createorder_action(request):
'''
獲取用戶輸入
'''
userName = request.POST.get("userName")
xxName = request.POST.get("xxName")
providerName = request.POST.get("providerName")
servicetype = request.POST.get('servicetype')
is_one_money = request.POST.get("is_one_money")
try:
'''
根據(jù)用戶輸入獲取請(qǐng)求需要的信息宪摧,
這里一般需要自己在應(yīng)用中封裝一些公用的方法/定義Const常量文件等引入當(dāng)前文件方便使用。
比如封裝函數(shù)根據(jù)輸入的用戶名獲取用戶id颅崩,
定義Const常量文件根據(jù)用戶選擇的服務(wù)匹配對(duì)應(yīng)常量值以避免重復(fù)寫(xiě)或手寫(xiě)出錯(cuò)
'''
utoken = gettoken(userName)
userId = User.objects.using('users').get(username=userName).fld_userid
docId = getDocId(docName)
xxId1 = getxxId1(xxId1)
xxId2 = createNewPatient(userId, xxName, utoken)
providerId = getProviderId(xxId1)
orderinfo = ''
response = ''
if servicetype == '1':
compactinfo = getCompactInfo(ProviderId, contract.ServicedefTypeConst.type1)
compactId = compactinfo.id
productId = compactinfo.productid
orderinfo = commitNetCase(userId, xxId2, providerId, productId, compactId, utoken)
# message=userId, patientId, doctorteamHotId, basicProductId, compactId, utoken
message = 'xx類(lèi)型訂單提交成功几于,訂單id為:' + str(orderinfo.get('orderId'))
response = render(request, 'createorder.html', {"message": message})
elif servicetype == '2':
......
elif servicetype == '3':
......
elif servicetype == '4':
......
elif servicetype == '6':
......
if is_one_money == '1':
if servicetype == '1' or servicetype == '2' or servicetype == '5' or servicetype == '6':
update_to_one_point(orderinfo.get("orderId"))
elif servicetype == '3':
update_to_one_point(orderinfo.get("xxx1"))
elif servicetype == '4':
update_to_one_point(orderinfo.get("xxx2"))
elif is_one_money == '0':
pass
else:
raise Exception("錯(cuò)誤的是否造1分錢(qián)訂單狀態(tài)")
return response
except InsecureRequestWarning:
if is_one_money == 1:
if servicetype == 1 or servicetype == 2 or servicetype == 5 or servicetype == 6:
update_to_one_point(orderinfo.get("orderId"))
elif servicetype == 3:
update_to_one_point(orderinfo.get("xxx"))
elif servicetype == 4:
update_to_one_point(orderinfo.get("xxx"))
elif is_one_money == 0:
pass
else:
raise Exception("錯(cuò)誤的是否造1分錢(qián)訂單狀態(tài)")
return response
except Exception as e:
message = '輸入信息有誤,或醫(yī)生不存在該服務(wù)沿后!'
response = render(request, 'createorder.html', {"message": message})
return response
五沿彭、配置路由
項(xiàng)目路由配置:
urlpatterns = [
# path('admin/', admin.site.urls),
path("index/",views.index),
......
path("order/", include("order.urls")),
......
]
應(yīng)用路由配置:
urlpatterns = [
path("createorder",unifiedorder.views.createorder),
path(r"createorder_action", unifiedorder.views.createorder_action),
]
六、大功告成尖滚,可以一鍵提交訂單啦
七喉刘、完善異常處理
進(jìn)行調(diào)試瞧柔、處理異常情況等,讓用戶輸入異常時(shí)頁(yè)面給出對(duì)應(yīng)提示