文章目录
1、引言
前面写过的一篇文章:【结合自动注册账号案例,详细剖析Python的pymysql模块使用】
里面的自动注册流程是第一版的,只能注册个人账号,后来需求有变更,新增了注册账号类型的选择,即注册流程有分【个人注册】和【企业注册】两种类型,不同的账号类型,在注册时,需要填写的参数有点区别,故需要对自动注册账号的脚本进行优化改造,下面将详细展开,同时也引申出在使用pymysql模块时遇到的问题及解决方案
2、需求分析
优化版本的注册功能,新增了注册账号类型的选择,整个自动化流程的顺序和关系如下图:
3、需求实现
完整代码如下:
import requests
import pymysql
import random
def auto_register(base_url, type, password=88888888, mobile=None, username=None):
"""连接数据库"""
print ( '开始连接数据库...' )
db = pymysql.connect (
host='xxx.xxx.x.xx',
port=3307,
user='xxxxxx',
passwd='xxxxxx',
db='xxxxxx',
autocommit=1 #PyMysql模块的连接对象默认是没有自动提交事务的,需要我们用一个commite()方法才能提交,此处添加autocommit = 1参数实现自动提交,就不用每次都commite()了
)
cur = db.cursor ()
"""【注册方式:1-个人账号、0-企业账号】"""
if type == 1 or mobile != None:
"""【查询手机号】"""
cur.execute ( "SELECT id FROM `wow_users` WHERE mobile = '{}'".format ( mobile ) )
userdata = cur.fetchall ()
if len ( userdata ) != 0:
print ( '当前手机号已注册' )
"""【更新手机号】"""
userid = userdata[0][0]
print ( '当前用户id是:{}'.format ( userid ) )
change_mobile = str ( mobile ) + '_' + str ( random.randint ( 1, 10000 ) )
print ( '更新手机号为:{}'.format ( change_mobile ) )
cur.execute ( "UPDATE `wow_users` SET `mobile`='{}' WHERE `id` = {}".format ( change_mobile, userid ) )
print ( '更新手机号成功' )
else:
print ( '当前手机号未注册' )
pass
"""【发送验证码】"""
from_data1 = {'mobile': mobile, 'sId': 5}
r1 = requests.post ( base_url + '/api/member/send-sms', data=from_data1 )
if r1.status_code == 200:
print ( '发送验证码成功' )
else:
print ( '发送验证码失败' )
"""【查询验证码】"""
cur = db.cursor ()
cur.execute("SELECT token FROM `wow_member_find` WHERE mobile = '{}' AND scenes = 5".format(mobile))
finddata = cur.fetchall()
code = finddata[0][0]
print('验证码是:{}'.format(code))
"""【注册】"""
from_data2 = {'is_personal': type, 'mobile': mobile, 'password0': password, 'password1': password, 'code': code}
r2 = requests.post ( base_url + '/api/member/register', data=from_data2 )
response = r2.json ()
if response['message'] == 'ok':
new_userid = response['data']['id']
print ( '注册成功!新注册的用户id是:{}'.format ( new_userid ) )
elif response['message'] == '手机验证码错误':
print ( '手机验证码错误,注册失败!' )
elif type == 0 or username != None:
"""【查询用户名】"""
cur.execute ( "SELECT * FROM `wow_users` WHERE username = '{}'".format ( username ) )
userdata = cur.fetchall ()
if len ( userdata ) != 0:
print ( '当前用户名已注册' )
"""【修改用户名】"""
userid = userdata[0][0]
print ( '当前用户id是:{}'.format ( userid ) )
change_username = str (username) + '_' + str ( random.randint ( 1, 10000 ) )
print ( '修改用户名为:{}'.format ( change_username ) )
cur.execute ( "UPDATE `wow_users` SET username = '{}' WHERE id = '{}'".format ( change_username, userid ) )
print ( '修改用户名成功' )
else:
print ( '当前用户名未注册' )
pass
"""【注册】"""
from_data3 = {'is_personal': 0, 'client_name': username, 'user_name': username, 'password0': password,
'password1': password}
r3 = requests.post ( base_url + '/api/member/register', data=from_data3 )
response = r3.json ()
if response['message'] == 'ok':
new_userid = response['data']['id']
print ( '注册成功!新注册的用户id是:{}'.format ( new_userid ) )
else:
print ( '注册失败!' )
"""关闭游标和关闭数据库连接"""
cur.close ()
db.close ()
print ( '关闭游标和数据库连接' )
if __name__ == '__main__':
auto_register ( 'https://xxx', '1', mobile=13800000004 )
4、知识拓展
4.1、Python 函数形参
4.1.1、Python 函数形参类型
- 必传参数:平时最常用的,也叫位置参数,必传确定数量的参数,传实参时位置要一一对应
- 默认参数:在调用函数时可以传也可以不传,如果参数没有默认值,在调用时必需为其指定一个值;如果参数有默认值,在调用时值是可选的,如果为其提供了一个值,将会覆盖默认值
- 可变参数:也称为不定长参数,就是传入的参数个数是可变的:*args,args是一个元组类型
- 关键字参数:**kwargs,长度可变,但是需要以 key-value 形式传参
由于新的注册流程中,有分【个人注册】和【企业注册】,不同的账号类型传参会不一样,所以在此次优化版的脚本中,定义函数时,使用了python函数形参中的【默认参数】的传递方式:
def auto_register(base_url, type, password=88888888, mobile=None, username=None)
此处参数mobile和username就是默认参数,默认值设为None,这样后面在调用该函数,就可以根据注册账号类型,有选择性的传实参
更多相关知识可以参考【Python函数参数操作详解】
4.1.2、Python 函数形参顺序
位置参数>默认参数>可变参数>关键字参数
4.2、pymysql的事务操作函数
在前面写过的一篇文章:【结合自动注册账号案例,详细剖析Python的pymysql模块使用】中有提到:当执行的sql语句是增、删、改时,还需要使用commit()方法才能够真正的操作数据库,该方法即起到了提交事务的操作,但是在本次优化的代码中已经去掉了这个方法,而是在连接数据库db = pymysql.connect ()时,添加了autocommit=1的参数,实现了自动提交事务的操作。
之所以这样改进,是因为在没有添加autocommit=1的参数时,代码走到查询验证码那一步时,查询到的验证码却不是当前发送的最新的验证码,而是上一次的验证码,即pymysql读取不到后台数据库更新的数据!经过网上搜索,说原因在于:
- Mysql的存储引擎InooDB的事务隔离级别默认是 可重复读(Repeatable Read),例如A客户端事务未提交,而B客户端事务修改了数据,A客户端只能读取到小于等于当前事务版本号的数据(快照读),所以只有提交完事务后,开启新的事务中才能读取到新的数据。
- pymysql模块的连接对象默认是没有自动提交事务的,需要我们用一个commite()方法才能提交,不像我们在MySQL客户端中,每次select,update,delete都帮我们自动提交事务,所以只要我们手动提交了事务,再重新select就可以查到新的数据。
那么对应的解决方法:
- 每次在用游标对象执行完查询语句后都手动commite()提交
- 创建一个自动帮我们提交事务的连接对象,即连接数据库pymysql.connect ()时添加参数autocommit=1
下面是总结一下pymysql的事务操作函数
4.2.1、事务提交:commit()
如果没有设置自动提交,则每次操作后必须提交事务,否则操作无效。
4.2.2、事务回滚:rollback()
操作出错时,可以用这个函数回滚到执行事务之前
一般事务提交和事务回滚是结合着使用:
try:
cur.execute(sql_1)
cur.execute(sql_2)
cur.execute(sql_3)
except Exception as e:
db.rollback() # 事务回滚
print('事务处理失败', e)
else:
db.commit() # 事务提交
print('事务处理成功', cur.rowcount)
4.2.3、自动提交事务:autocommit=True
用pymysql.connect()连接数据库时,连接对象默认是没有自动提交事务的,即autocommit=False,如果想实现自动提交事务,则应设置autocommit=True或autocommit=1。
db = pymysql.connect (
host='xxx.xxx.x.xx',
port=3307,
user='xxxxxx',
passwd='xxxxxx',
db='xxxxxx',
autocommit=1 # 实现自动提交事务
)