Records 源码阅读与实践
简介
Records 是用于对大多数关系型数据库进行原始的 SQL 语句查询的第三方库,由 kennethreitz 创建,目前仅 500 多行代码,非常简单但又十分强大。它支持的数据库有 RedShift, Postgres, MySQL, SQLite, Oracle, and MS-SQL。
Github:https://github.com/kennethreitz/records
SQL for Humans™ https://pypi.python.org/pypi/records/
主目录 | 描述 |
---|---|
examples | 使用示例文件 |
tests | 测试代码文件 |
.gitgnore | 配置 Git 需要忽略的文件 |
.travis.yml | 持续集成工具 Travis CI 的配置文件,用于在代码发生变更时自动运行构建、测试 |
HISTORY.rst | .rst 文件是一种纯文本文件,全称reStructuredText,此处为更新记录 |
LICENSE | 开源许可证,records 使用 ISC 许可,功能上与两句版 BSD 许可和 MIT 许可一致 |
MANIFESR.in | 指定需要打包的文件,因为 python 打包程序默认不打包一些文本文档,所以需要在此处指定 |
Makefile | make 命令工具,此处定义了一些shell 脚本命令用于发布、测试等 |
Pipfile | Pipfile 与 Pipfile.lock 是 Pipenv 生成依赖管理文件,用于替代过于简陋的 requirements.txt 文件 |
Pipfile.lock | Pipfile.lock 是根据 Pipfile 和当前环境自动生成的 JSON 格式的依赖文件,任何情况下都不要手动修改该文件 |
README.rst | README 文件,关于 Records 简单介绍 |
records.py | Records 源码文件 |
setup.py | 用于 python 包构建工具的代码文件 |
tox.ini | tox是通用的虚拟环境管理和测试命令行工具,此处用于测试records 在不同 python 版本的兼容性 |
records.py
以下是按照源代码顺序进行的阅读记录,如有错误,欢迎指正!
# -*- coding: utf-8 -*-
PEP 0263:建议在Python文件头部声明使用何种编码
import
import os
# 用于处理文件和目录
from sys import stdout
# Python 默认输出
from collections import OrderedDict
# OrderedDict 有序字典,根据元素放入先后顺序排列
from contextlib import contextmanager
# 上下文管理器
from inspect import isclass
# isclass 判断是否为类对象
import tablib
# 将数据输出为常用格式的第三方库
from docopt import docopt
# 解析命令行参数
from sqlalchemy import create_engine, exc, inspect, text
# sqlalchemy 提供 SQL 工具和 ORM 工具
什么是ORM?
全称 Object-Relationl Mapping,在Python中表现为关系型数据库的对象和Python对象之间的映射,有了这个映射,我们就可以直接通过调用Python对象来操作数据库。
isexception 方法
# isexception 判断对象是否为Exception类实例或其子类
def isexception(obj):
"""Given an object, return a boolean indicating whether it is an instance
or subclass of :py:class:`Exception`.
"""
if isinstance(obj, Exception):
return True
if isclass(obj) and issubclass(obj, Exception):
return True
return False
Record 类
# Record 储存单行数据的类
class Record(object):
"""A row, from a query, from a database."""
__slots__ = ('_keys', '_values') # 限制类的合法属性集(仅对新式类作用)
def __init__(self, keys, values):
self._keys = keys # 单下划线保护变量
self._values = values
# Ensure that lengths match properly.
# assert 断言,表达式返回为False则报错,此处为确保 _keys _values 长度一致
assert len(self._keys) == len(self._values)
# keys 和 values 都是 getter 方法,是用于获取保护变量值的函数
def keys(self):
"""Returns the list of column names from the query."""
return self._keys
def values(self):
"""Returns the list of values from the query."""
return self._values
# __repr__ 定义对象在终端返回的字符串形式
def __repr__(self):
return '<Record {}>'.format(self.export('json')[1:-1])
# __getitem__ 定义 obj[key] 索引返回值
def __getitem__(self, key):
# Support for index-based lookup.
# 整数索引支持
if isinstance(key, int): # 如果 key 为整数类型
return self.values()[key]
# Support for string-based lookup.
# 字符索引支持
if key in self.keys(): # 如果 key 为 keys属性中的值
i = self.keys().index(key) # 返回该 key 的位置
if self.keys().count(key) > 1: # 如果 key 在 keys 属性中有多值
raise KeyError("Record contains multiple '{}' fields.".format(key))
return self.values()[i]
raise KeyError("Record contains no '{}' field.".format(key))
# __getattr__ 属性查找,定义 obj.key 返回值
def __getattr__(self, key):
try:
return self[key]
except KeyError as e:
raise AttributeError(e)
# __dir__ 定义 obj 属性信息,通过 dir(obj) 调用返回
def __dir__(self):
standard = dir(super(Record, self))
# Merge standard attrs with generated ones (from column names).
return sorted(standard + [str(k) for k in self.keys()])
# get 实现类似于字典的 get 方法,若找不到值则返回默认值
def get(self, key, default=None):
"""Returns the value for a given key, or default."""
try:
return self[key]
except KeyError:
return default
# as_dict 生成字典,ordered 控制是否返回有序字典
def as_dict(self, ordered=False):
"""Returns the row as a dictionary, as ordered."""
items = zip(self.keys(), self.values())
return OrderedDict(items) if ordered else dict(items)
# @property 装饰器,将函数调用方式改为属性调用方式(obj.dataset)
# dataset 将 keys 和 values 放入 tablib 的 Dataset 对象
@property
def dataset(self):
"""A Tablib Dataset containing the row."""
data = tablib.Dataset()
data.headers = self.keys()
# _reduce_datetimes 在后面有定义,若 values 为 datetime 类型则转换为字符串
row = _reduce_datetimes(self.values())
data.append(row)
return data
# export 调用Dataset对象的export方法输出指定格式数据
def export(self, format, **kwargs):
"""Exports the row to the given format."""
return self.dataset.export(format, **kwargs)
Record 对象储存的是单条记录,keys 负责储存列名,values 负责储存每列对应的值,支持通过整数、字符串、get 方法索引,可以通过 tablib 导出为指定格式数据。