Dataset中SQLAlchemy如何使用

37次阅读
没有评论

共计 11272 个字符,预计需要花费 29 分钟才能阅读完成。

这篇文章给大家介绍 Dataset 中 SQLAlchemy 如何使用,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

Dataset 对于操作 JSON、CSV 文件、NoSQL 非常好用。

import dataset

连接 MySQL 数据库:

db = dataset.connect(mysql://username:password@10.10.10.10/ctf?charset=utf8)

用户名:username,密码:password,数据库地址(地址 + 端口):10.10.10.10,database 名: ctf

连接 SQLite 数据库:

db = dataset.connect(sqlite:///ctf.db)

连接 PostgreSQL 数据库:

db = dataset.connect(postgresql://scott:tiger@localhost:5432/mydatabase)

一定要注意指定字符编码

table = db[city] #(选择 city 表) user = table(name) #  找出表中 name 列属性所有数据  res = db.query(select name from table limit 10) #  如果不需要查看全部数据的话 *** 用 limit,因为全部数据的载入非常非常耗时间  for x in res: print x[name] #  选 name 字段的数据  table.insert(dict(name= John Doe , age=37)) table.insert(dict(name= Jane Doe , age=34, gender= female)) john = table.find_one(name= John Doe)

在数据库中查找是否有同时满足多个条件的数据:table.find_one(属性 1 = 属性值 1, 属性 2 = 属性值 2, hellip;)

注:find_one 速度很慢

插入数据

dataset 会根据输入自动创建表和字段名

table = db[user] #  或者 table = db.get_table(user) table.insert(dict(name= John Doe , age=46, country= China)) table.insert(dict(name= Jane Doe , age=37, country= France , gender= female)) #  主键 id 自动生成

更新数据

table.update(dict(name= John Doe , age=47), [name]) #  第二个参数相当于 sql update 语句中的 where,用来过滤出需要更新的记录

事务操作

事务操作可以简单的使用上下文管理器来实现, 出现异常,将会回滚

with dataset.connect() as tx: tx[ user].insert(dict(name= John Doe , age=46, country= China)) #  相当于: db = dataset.connect() db.begin() try: db[user].insert(dict(name= John Doe , age=46, country= China)) db.commit() except: db.rollback() #  也可以嵌套使用: db = dataset.connect() with db as tx1: tx1[ user].insert(dict(name= John Doe , age=46, country= China)) with db as tx2: tx2[user].insert(dict(name= Jane Doe , age=37, country= France , gender= female))

从表获取数据

users = db[user].all() for user in db[ user]: # print(user[ age]) # chinese_users = user.find(country= China) john = user.find_one(name= John Doe)

获取非重复数据

db[user].distinct(country)

删除记录

table.delete(place= Berlin)

执行 SQL 语句

result = db.query(SELECT country, COUNT(*) c FROM user GROUP BY country ) for row in result: print(row[ country], row[c])

导出数据

result = db[users].all() dataset.freeze(result, format= json , filename= users.json)

JSON

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,非常易于人阅读和编写。

import json

json.dumps 将 Python 对象编码成 JSON 字符串

json.loads 将已编码的 JSON 字符串解码为 Python 对象

MySQL 数据库:

分类表 -categories,包括类别 web,reversing,crypto(加解密),mic 等

题目表 -tasks, 包括题目 id,题目名,flag, 分值,文件 地址,题目等级,题目详细描述

flag 表 -flag, 包括题目 id, 用户 id, 得分,时间戳

用户表 -users,包括用户 id, 用户名,密码

题目分类表 -cat_task, 包括题目 id, 题目类别 id

flag 表中每条数据由于是有题目 ID task_id 和用户 ID user_id 来共同确认的,所以采用复合主键:primary key  (task_id,user_id)

联合主键和复合主键的区别

python 装饰器

Decorator 通过返回包装对象实现间接调用, 以此插入额外逻辑

https://www.zhihu.com/question/26930016

wraps 本身也是一个装饰器,它能把原函数的元信息拷贝到装饰器函数中,这使得装饰器函数也有和原函数一样的元信息了

from functools import wraps def logged(func): @wraps(func) def with_logging(*args,**kwargs): print func.__name__ +  was called  return func(*args,**kwargs) return with_logging @logged def f(x):  does some math  return x + x * x print f.__name__ # prints  f  print f.__doc__ # prints  does some math

web 框架采用 flask

from flask import Flask

引入 Flask 类,Flask 类实现了一个 WSGI(Web Server Gateway Interface)应用

app = Flask(__name__)

app 是 Flask 的实例,它接收包或者模块的名字作为参数,但一般都是传递__name__

@app.route(/) def hello_world(): return  Hello World!

使用 app.route 装饰器会将 URL 和执行的视图函数的关系保存到 app.url_map 属性上。处理 URL 和视图函数的关系的程序就是路由,这里的视图函数就是 hello_world

if __name__ ==  __main__ : app.run(host= 0.0.0.0 ,port=9000)

使用这个判断可以保证当其他文件引用这个文件的时候 (例如 from hello import  app) 不会执行这个判断内的代码,也就是不会执行 app.run 函数。

执行 app.run 就可以启动服务了。默认 Flask 只监听虚拟机的本地 127.0.0.1 这个地址,端口为 5000。而我们对虚拟机做的端口转发端口是 9000,所以需要制定 host 和 port 参数,0.0.0.0 表示监听所有地址,这样就可以在本机访问了。

服务器启动后,会调用 werkzeug.serving.run_simple 进入轮询,默认使用单进程单线程的 werkzeug.serving.BaseWSGIServer 处理请求,实际上还是使用标准库 BaseHTTPServer.HTTPServer,通过 select.select 做 0.5 秒的 while  TRUE 的事件轮询。当我们访问 http://127.0.0.1:9000/, 通过 app.url_map 找到注册的 / 这个 URL 模式, 就找到了对应的 hello_world 函数执行,返回 hello  world!, 状态码为 200。如果访问一个不存在的路径,如访问 http://127.0.0.1:9000/a,Flask 找不到对应的模式,就会向浏览器返回 Not  Found,状态码为 404

flask 中 jsonify 的作用

jsonify 的作用实际上就是将我们传入的 json 形式数据序列化成为 json 字符串,作为响应的 body,并且设置响应的 Content-Type 为 application/json,构造出响应返回至客户端

效果等于 json.dumps

jsonify 的 Content-Type 字段值为 application/json

json.dumps 的 Content-Type 字段值为 text/html

修改 flask 中静态文件夹

修改的 flask 默认的 static 文件夹只需要在创建 Flask 实例的时候, 把 static_folder 和 static_url_path 参数设置为空字符串即可。

app = Flask(__name__, static_folder=”, static_url_path=”)

访问的时候用 url_for 函数,res 文件夹和 static 文件夹同一级:

url_for(lsquo;static rsquo;, filename= rsquo;res/favicon.ico rsquo;)

werkzeug

werkzeug 是一个 WSGI 工具包,可以作为一个 Web 框架的底层库。它封装好了很多 Web 框架的东西,例如  Request,Response 等等。Flask 框架就是一 Werkzeug 为基础开发的

generate_password_hash(password)

将用户输入的明文密码加密成密文进行存储

密码加盐哈希函数。用来将明文密码加密,返回加密后的密文,用来进行用户注册

函数定义:

werkzeug.security.generate_password_hash(password, method= pbkdf2:sha1 ,  salt_length=8)

密文格式:method$salt$hash

password: 明文密码

method: 哈希的方式(需要是 hashlib 库支持的),格式为

pbpdf2: method [:iterations]。参数说明:

method:哈希的方式,一般为 SHA1,

iterations:(可选参数)迭代次数,默认为 1000。

slat_length: 盐值的长度,默认为 8

check_password_hash(hash,password)

验证经过 generate_password_hash 哈希的密码,将明文和密文进行比较, 查看是否一致,用来验证用户登录

函数定义:

werkzeug.security.check_password_hash(pwhash, password)

pwhash: generate_password_hash 生成的哈希字符串

password: 需要验证的明文密码

flask 中的 session

rom flask import session user = db[users].find_one(username=username) session[user_id] = user[id]

由于使用了 session,所以需要设置一个 secret_key 用来做一些模块的 hash

Flask Web Development 中的内容:

SECRET_KEY 配置变量是通用密钥, 可在 Flask 和多个第三方扩展中使用。如其名所示, 加密的强度取决于变量值的机密度。不同的程序要使用不同的密钥, 而且要保证其他人不知道你所用的字符串。

SECRET_KEY 的作用主要是提供一个值做各种 HASH,   是在其加密过程中作为算法的一个参数(salt 或其他)。所以这个值的复杂度也就影响到了数据传输和存储时的复杂度。

flask 变量规则

要给 URL 添加变量部分,你可以把这些特殊的字段标记为 variable_name,  这个部分将会作为命名参数传递到你的函数。规则可以用 converter:variable_name 指定一个可选的转换器

@route(/hello/ name) def index(name): return  b Hello {{name}} /b !

数据库查询

对 dataset 的数据查询,使用冒号来为变量传参。

select f.task_id from flags f where f.user_id =  :user_id”rsquo;,user_id=session[lsquo;user_id rsquo;])

模板渲染

使用 render_template 方法来渲染模板。将模板名和你想作为关键字的参数传入模板的变量

MySQL

IFNULL(expr1,expr2)

如果 expr1 不是 NULL,IFNULL()返回 expr1,否则它返回 expr2。

IFNULL()返回一个数字或字符串值,取决于它被使用的上下文环境。

max 函数是用来找出记录集中 *** 值的记录

对于 left join,不管 on 后面跟什么条件,左表的数据全部查出来,因此要想过滤需把条件放到 where 后面

对于 inner join,满足 on 后面的条件表的数据才能查出,可以起到过滤作用。也可以把条件放到 where 后面

在使用 left jion 时,on 和 where 条件的区别如下:

on 条件是在生成临时表时使用的条件,它不管 on 中的条件是否为真,都会返回左边表中的记录。

where 条件是在临时表生成好后,再对临时表进行过滤的条件。这时已经没有 left  join 的含义 (必须返回左边表的记录) 了,条件不为真的就全部过滤掉。

order by 的用法  

使用 order by,一般是用来,依照查询结果的某一列 (或多列) 属性,进行排序(升序:ASC; 降序:DESC; 默认为升序)。

当排序列含空值时:

ASC:排序列为空值的元组 *** 显示。

DESC:排序列为空值的元组 *** 显示。

可以把 null 值看做无穷大

select * from s order by sno desc, sage asc

group by 的用法

group by 按照查询结果集中的某一列(或多列),进行分组,值相等的为一组

1、细化集函数 (count,sum,avg,max,min) 的作用对象:

未对查询结果分组,集函数将作用于整个查询结果。

对查询结果分组后,集函数将分别作用于每个组。

SELECT cno,count(sno) from sc group by cno

2、GROUP BY 子句的作用对象是查询的中间结果表

分组方法:按指定的一列或多列值分组,值相等的为一组。

使用 GROUP BY 子句后,SELECT 子句的列名列表中只能出现分组属性 (比如:sno) 和集函数(比如:count())

select sno,count(cno) from sc group by sno

3、多个列属性进行分组

select cno,grade,count(cno) from sc group by cno,grade

4、使用 HAVING 短语筛选最终输出结果

只有满足 HAVING 短语指定条件的组才输出。

HAVING 短语与 WHERE 子句的区别:作用对象不同。

1、WHERE 子句作用于基表或视图,从中选择满足条件的元组。

2、HAVING 短语作用于组,从中选择满足条件的组

select sno from sc group by sno having count(cno) 3

select sno,count(cno) from sc where grade 60 group by sno having  count(cno) 3

MySQL 的左连接、右连接、等值连接

1. 左连接(left join)

select m.columnname hellip; hellip;,n.* columnname hellip;.. from left_table m left join right_table n on m.columnname_join=n.columnname_join and n.columnname=xxx where m.columnname=xxx hellip;..

ON 是连接条件,用于把 2 表中等值的记录连接在一起,但是不影响记录集的数量。若是表 left_table 中的某记录,无法在表 right_table 找到对应的记录,则此记录依然显示在记录集中,只是表 right_table 需要在查询显示的列的值用 NULL 替代;

ON 连接条件中表 n.columnname=xxx 用于控制 right_table 表是否有符合要求的列值还是用 NULL 替换的方式显示在查询列中,不影响记录集的数量;

WHERE 字句控制记录是否符合查询要求,不符合则过滤掉

2. 右连接(right join)

select m.columnname hellip; hellip;,n.* columnname hellip;.. from left_table m right join right_table n on m. columnname_join=n. columnname_join and m. columnname=xxx where n.columnname=xxx hellip;..

3. 等值连接

select m.columnname hellip; hellip;,n.* columnname hellip;.. from left_table m [inner] join right_table n on m. columnname_join=n. columnname_join where m.columnname=xxx hellip;.. and n.columnname=xxx hellip;.

或者

select m.columnname hellip; hellip;,n.* columnname hellip;.. from left_table m , right_table n where m. columnname_join=n. columnname_join and m.columnname=xxx hellip;.. and n.columnname=xxx hellip;.

ON 是连接条件,不再与左连接或右连接的功效一样,除了作为 2 表记录匹配的条件外,还会起到过滤记录的作用,若 left_table 中记录无法在 right_table 中找到对应的记录,则会被过滤掉;

WHERE 字句,不管是涉及表 left_table、表 right_table 上的限制条件,还是涉及 2 表连接的条件,都会对记录集起到过滤作用,把不符合要求的记录刷选掉;

jinja2 获取循环索引

jinja2 获取循环 {% for i in n %} 的索引使用 loop.index

{% for i in names %}  tr   td {{ loop.index }} /td  // 当前是第 x 条   td {{ i.name }} /td   /tr  {% endfor %}

flask 重定向和错误

可以用 redirect()函数把用户重定向到其它地方。放弃请求并返回错误代码,用 abort()函数。

from flask import abort, redirect, url_for @app.route(/) def index(): return redirect(url_for( login)) @app.route(/login) def login(): abort(401) this_is_never_executed()

默认情况下,错误代码会显示一个黑白的错误页面。如果你要定制错误页面,可以使用 errorhandler()

装饰器:

from flask import render_template @app.errorhandler(404) def page_not_found(error): return render_template(page_not_found.html), 404

注意 render_template()调用之后的 404。这告诉 Flask,该页的错误代码是 404  ,即没有找到。默认为 200,也就是一切正常。

flask CSRF 防护机制

@app.before_request def csrf_protect(): if request.method ==  POST : token = session.pop( _csrf_token , None) if not token or token != request.form.get(_csrf_token): abort(403) def some_random_string(): return hashlib.sha256(os.urandom(16).hexdigest()) def generate_csrf_token(): if  _csrf_token  not in session: session[ _csrf_token] = some_random_string() return session[ _csrf_token]

在 flask 的全局变量里面注冊 上面那个生成随机 token 的函数

app.jinja_env.globals[lsquo;csrf_token rsquo;] = generate_csrf_token

在网页的模板是这么引入的

form method=post action=   input name=_csrf_token type=hidden value= {{ csrf_token() }}

flask 上下文处理器

Flask   上下文处理器自动向模板的上下文中插入新变量。上下文处理器在模板渲染之前运行,并且可以在模板上下文中插入新值。上下文处理器是一个返回字典的函数,这个字典的键值最终将传入应用中所有模板的上下文:

@app.context_processor def inject_user(): return dict(user=g.user)

上面的上下文处理器使得模板可以使用一个名为 user 值为 g.user 的变量。不过这个例子不是很有意思,因为 g 在模板中本来就是可用的,但它解释了上下文处理器是如何工作的。

变量不仅限于值,上下文处理器也可以使某个函数在模板中可用(由于 Python 允许传递函数):

@app.context_processor def utility_processor(): def format_price(amount, currency=u euro;): return u {0:.2f}{1}.format(amount, currency) return dict(format_price=format_price)

上面的上下文处理器使得 format_price 函数在所有模板中可用:

{{format_price(0.33) }}

日志记录

handler = logging.FileHandler(lsquo;flask.log rsquo;, encoding= rsquo;UTF-8 prime;)

1、请求之前设置 requestId 并记录日志

每个 URL 请求之前,定义 requestId 并绑定到 g

@app.before_request def before_request(): g.requestId = gen_requestId() logger.info(Start Once Access, and this requestId is %s  % g.requestId)

2、请求之后添加响应头与记录日志

每次返回数据中,带上响应头,包含 API 版本和本次请求的 requestId,以及允许所有域跨域访问 API, 记录访问日志

@app.after_request def add_header(response): response.headers[X-SaintIC-Media-Type] =  saintic.v1  response.headers[X-SaintIC-Request-Id] = g.requestId response.headers[Access-Control-Allow-Origin] =  *  logger.info(json.dumps({  AccessLog : {  status_code : response.status_code,  method : request.method,  ip : request.headers.get( X-Real-Ip , request.remote_addr),  url : request.url,  referer : request.headers.get(Referer),  agent : request.headers.get(User-Agent),  requestId : str(g.requestId), } } )) return response

basicConfig 方法可以满足你在绝大多数场景下的使用需求,但是 basicConfig 有一个很大的缺点。调用 basicConfig 其实是给 root  logger 添加了一个 handler(FileHandler),这样当你的程序和别的使用了  logging 的第三方模块一起工作时,会影响第三方模块的 logger 行为。这是由 logger 的继承特性决定的

logging.basicConfig(level=logging.DEBUG, format= %(asctime)s %(levelname)s %(message)s , datefmt= %a, %d %b %Y %H:%M:%S , filename= logs/pro.log , filemode= w ) logging.debug(dddddddddd)

MySQL 字符编码

除了设置数据库的之外,由于 dataset 默认创建数据库和表的字符集不是 utf8, 所以需要自己设置,否则会中文乱码,所以需要修改表的字符集

my.cnf [client] default-character-set=utf8 [mysqld] character-set-server=utf8 collation-server=utf8_general_ci default-storage-engine=INNODB

表的字符集

show create table tasks; alter table tasks convert to character set utf8;

关于 Dataset 中 SQLAlchemy 如何使用就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-08-03发表,共计11272字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)