lightweight SQLAlchemy based ORM
Python
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
thing
.gitignore
MANIFEST.in
README.md
setup.py

README.md

     /\  \         /\__\          ___        /\__\         /\  \    
     \:\  \       /:/  /         /\  \      /::|  |       /::\  \   
      \:\  \     /:/__/          \:\  \    /:|:|  |      /:/\:\  \  
      /::\  \   /::\  \ ___      /::\__\  /:/|:|  |__   /:/  \:\  \ 
     /:/\:\__\ /:/\:\  /\__\  __/:/\/__/ /:/ |:| /\__\ /:/__/_\:\__\
    /:/  \/__/ \/__\:\/:/  / /\/:/  /    \/__|:|/:/  / \:\  /\ \/__/
   /:/  /           \::/  /  \::/__/         |:/:/  /   \:\ \:\__\  
   \/__/            /:/  /    \:\__\         |::/  /     \:\/:/  /  
                   /:/  /      \/__/         /:/  /       \::/  /   
                   \/__/                     \/__/         \/__/    

Thing 是什么?

Thing是一个基于SQLAlchemy的配置简单、使用简单且灵活的ORM。

使用方法

举个简单的例子,假如有3个表:comment, post, user, 3个表的字段分别是:

comment表:

+---------+------------------+------+-----+---------+----------------+
| Field   | Type             | Null | Key | Default | Extra          |
+---------+------------------+------+-----+---------+----------------+
| id      | int(11) unsigned | NO   | PRI | NULL    | auto_increment |
| user_id | int(11)          | YES  | MUL | NULL    |                |
| post_id | int(11)          | YES  | MUL | NULL    |                |
| content | text             | YES  |     | NULL    |                |
+---------+------------------+------+-----+---------+----------------+

post表:

+---------+------------------+------+-----+---------+----------------+
| Field   | Type             | Null | Key | Default | Extra          |
+---------+------------------+------+-----+---------+----------------+
| id      | int(11) unsigned | NO   | PRI | NULL    | auto_increment |
| user_id | int(11)          | YES  | MUL | NULL    |                |
| created | int(11)          | YES  |     | NULL    |                |
| content | text             | YES  |     | NULL    |                |
| title   | varchar(255)     | YES  |     | NULL    |                |
+---------+------------------+------+-----+---------+----------------+

user表:

+-------+------------------+------+-----+---------+----------------+
| Field | Type             | Null | Key | Default | Extra          |
+-------+------------------+------+-----+---------+----------------+
| id    | int(11) unsigned | NO   | PRI | NULL    | auto_increment |
| name  | varchar(30)      | YES  |     | NULL    |                |
+-------+------------------+------+-----+---------+----------------+

定义Model

先来看看目录结构

├── __init.py__
├── conn.py # 用于数据库连接
├── models
│   ├── __init__.py
│   ├── comment.py
│   ├── post.py
│   ├── user.py
└── test.py

test.py就是进行测试的地方,先来看看各个model的内容:

comment.py

from thing import thing

class Comment(thing.Thing):
    _belongs_to = {
            'post': {
                'model': 'models.post.Post',
                'foreign_key': 'post_id',
                },
            'author': {
                'model': 'models.user.User',
                'foreign_key': 'user_id',
                },
            }

post.py

from thing import thing

class Post(thing.Thing):
    _belongs_to = {
            'author': {
                'model': 'models.user.User',
                'foreign_key': 'user_id',
                }
            }
    _has_many = {
            'comments': {
                'model': 'models.comment.Comment',
                'foreign_key': 'user_id',
                }
            }

user.py

from thing import thing

class User(thing.Thing):
    _has_many = {
            'posts': {
                'model': 'models.post.Post',
                'foreign_key': 'user_id'
                },
            'comments': {
                'model':  'models.comment.Comment',
                'foreign_key': 'user_id'
                }
            }

再来看看conn.py

conn.py

from thing import thing

config = {
        'db': {
            'master': {
                'url': 'mysql://root:123456@127.0.0.1:3306/test?charset=utf8',
                'echo': False,
                },
            'slave': {
                'url': 'mysql://root:123456@127.0.0.1:3306/test?charset=utf8',
                'echo': False,
                },
            },
        'redis': {
            'host': 'localhost',
            'port': 6379,
            'db': 1,
            },
        'thing': {
            'debug': True,
            }
        }

thing.Thing.config(config)

OK,万事具备,开工!

import conn
from models.comment import Comment
from models.user import User
from models.post import Post

# -------- 插入数据 --------
user = User()
user.name = 'foo'
user.save()
# 或者 user = User(name='foo').save()

# -------- 获取数据 --------
user = User().find(1)
print user.name

# -------- 获取关联数据 -------
posts = User().find(1).posts.findall()
# 如果要设置offset / limit, 在findall里加入参数即可
# posts = User().find(1).posts.findall(offset = 0, limit = 20)

# ------- 删除数据 -------
User().find(1).delete()

# ------- 更新数据 -------
user = User().find(1)
user.name = 'bar'
user.save()

动态查询

这个是受Rails影响,觉得很方便就拿来了。比如 Post().count_by_user_id(3),就可以找到user_id为3的用户发表的文章数量。要获取user_id为3的用户发表的文章,可以Post().findall_by_user_id(3, limit=20),比起Post().where('user_id', '=', 3).findall()更加简洁和明了。

关于性能和缓存

Thing内置了Redis作为缓存,你甚至都不需要知道Redis的存在,正常该怎么用还怎么用,Thing会自动处理缓存的生成、读取、过期、删除等操作。

假设表post里有5条数据,在获取每条post后,还想获取该post对应的用户信息,代码如下:

posts = Post().findall(limit=5)

for post in posts:
	print post.author

在开启Debug的情况下,可以在终端看到如下显示:

DEBUG - [cost:0.0032] - SELECT post.id, post.user_id, post.created, post.content, post.title
FROM post ORDER BY post.id DESC
LIMIT :param_1 OFFSET :param_2
DEBUG - Cache Read: thing.User:1
{u'id': 1, u'name': u'lzyy'}
DEBUG - Cache Read: thing.User:1
{u'id': 1, u'name': u'lzyy'}
DEBUG - Cache Read: thing.User:1
{u'id': 1, u'name': u'lzyy'}
DEBUG - Cache Read: thing.User:1
{u'id': 1, u'name': u'lzyy'}
DEBUG - Cache Read: thing.User:1
{u'id': 1, u'name': u'lzyy'}

可以看到用户的信息都是从缓存中读取的,所以不用担心n+1的问题。 假如用户的信息被更新,缓存也会自动更新。

其他

  • 配置信息里的masterslave为必选项,可以相同。Thing会根据不同的查询,自动找到对应的db。如find/findall会找slave,update/delete会找master。
  • 配置信息里的redis项为必选项。
  • 动态查询目前支持find_by, findall_by, findall_in, count_by
  • 内置了8个钩子,会在相应的事件发生时被调用,分别是:_before_insert,_after_insert,_before_update,_after_update,_before_delete,_after_delete,_before_find,_after_find,可以在子类里覆盖这些方法来实现自己的逻辑。
  • 复杂的SQL可以使用execute方法,返回的结果是SQLAlchemy的ResultProxy
  • 如果要一次更新多处的话,可以使用updateall方法,Post().where('user_id', '=', 1).updateall(user_id=2)
  • 表名如果和小写的类名不一样的话,可以在子类里重新设置_tablename
  • 每个表一定要有主键,默认为id,可以在子类里重新设置_primary_key
  • 支持has_many和belongs_to,可以在子类里定义_has_many_belongs_to
  • 没有join方法

ChangeLog

0.3.3

  • 修复无法从pip安装的bug
  • 修复安装时对redis-py的依赖
  • import thing变为from thing import thing

0.3.2

  • 修复了并发情况下会出现「Exception _mysql_exceptions.ProgrammingError: (2014, "Commands out of sync; you can't run this command now"」错误。
  • Redis缓存变为可配置项。如果不想要Redis的话,在config里取消Redis配置即可。

0.3.1

  • 取消了对Validation的支持
  • 取消了对Sharding和Partition的支持
  • 取消了事件分发机制