├── README.md ├── data_handler.py └── test.py /README.md: -------------------------------------------------------------------------------- 1 | QuickORM 2 | ======== 3 | 4 | A simple ORM provides elegant API for Python-MySQL operation 5 | 6 | Connect to MySQL 7 | ---------------- 8 | 9 | ```python 10 | from data_handler import Database 11 | 12 | db_config = { 13 | 'host': 'localhost', 14 | 'port': 3306, 15 | 'user': 'root', 16 | 'password': '123456', 17 | 'database': 'test' 18 | } 19 | Database.connect(**db_config) 20 | ``` 21 | 22 | Define a model 23 | -------------- 24 | 25 | ```python 26 | from data_handler import Model, Field 27 | 28 | class TestModel(Model): 29 | db_table = 'test' 30 | a = Field() 31 | b = Field() 32 | ``` 33 | 34 | Insert 35 | ------ 36 | 37 | ```python 38 | test = TestModel() 39 | test.a = 5 40 | test.b = 'john' 41 | test.save() 42 | ``` 43 | 44 | Query 45 | ----- 46 | 47 | ```python 48 | for r in TestModel.where(a=5, b='john').select(): 49 | print r.a 50 | print r.b 51 | ``` 52 | 53 | Count 54 | ----- 55 | 56 | ```python 57 | print TestModel.where(a=5, b='john').count() 58 | ``` 59 | 60 | Update 61 | ------ 62 | 63 | ```python 64 | TestModel.where(a=5, b='john').update(a=1) 65 | ``` 66 | 67 | Execute raw SQL 68 | --------------- 69 | 70 | ```python 71 | from data_handler import execute_raw_sql 72 | 73 | results = execute_raw_sql('select b, count(*) from test where b = %s group by b;', (1,)) 74 | for val, cnt in results: 75 | print val, cnt 76 | ``` -------------------------------------------------------------------------------- /data_handler.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import MySQLdb 4 | 5 | 6 | class Field(object): 7 | pass 8 | 9 | 10 | class Expr(object): 11 | def __init__(self, model, kwargs): 12 | self.model = model 13 | # How to deal with a non-dict parameter? 14 | self.params = kwargs.values() 15 | equations = [key + ' = %s' for key in kwargs.keys()] 16 | self.where_expr = 'where ' + ' and '.join(equations) if len(equations) > 0 else '' 17 | 18 | def update(self, **kwargs): 19 | _keys = [] 20 | _params = [] 21 | for key, val in kwargs.iteritems(): 22 | if val is None or key not in self.model.fields: 23 | continue 24 | _keys.append(key) 25 | _params.append(val) 26 | _params.extend(self.params) 27 | sql = 'update %s set %s %s;' % ( 28 | self.model.db_table, ', '.join([key + ' = %s' for key in _keys]), self.where_expr) 29 | return Database.execute(sql, _params) 30 | 31 | def limit(self, rows, offset=None): 32 | self.where_expr += ' limit %s%s' % ( 33 | '%s, ' % offset if offset is not None else '', rows) 34 | return self 35 | 36 | def select(self): 37 | sql = 'select %s from %s %s;' % (', '.join(self.model.fields.keys()), self.model.db_table, self.where_expr) 38 | for row in Database.execute(sql, self.params).fetchall(): 39 | inst = self.model() 40 | for idx, f in enumerate(row): 41 | setattr(inst, self.model.fields.keys()[idx], f) 42 | yield inst 43 | 44 | def count(self): 45 | sql = 'select count(*) from %s %s;' % (self.model.db_table, self.where_expr) 46 | (row_cnt, ) = Database.execute(sql, self.params).fetchone() 47 | return row_cnt 48 | 49 | 50 | class MetaModel(type): 51 | db_table = None 52 | fields = {} 53 | 54 | def __init__(cls, name, bases, attrs): 55 | super(MetaModel, cls).__init__(name, bases, attrs) 56 | fields = {} 57 | for key, val in cls.__dict__.iteritems(): 58 | if isinstance(val, Field): 59 | fields[key] = val 60 | cls.fields = fields 61 | cls.attrs = attrs 62 | 63 | 64 | class Model(object): 65 | __metaclass__ = MetaModel 66 | 67 | def save(self): 68 | insert = 'insert ignore into %s(%s) values (%s);' % ( 69 | self.db_table, ', '.join(self.__dict__.keys()), ', '.join(['%s'] * len(self.__dict__))) 70 | return Database.execute(insert, self.__dict__.values()) 71 | 72 | @classmethod 73 | def where(cls, **kwargs): 74 | return Expr(cls, kwargs) 75 | 76 | 77 | class Database(object): 78 | autocommit = True 79 | conn = None 80 | db_config = {} 81 | 82 | @classmethod 83 | def connect(cls, **db_config): 84 | cls.conn = MySQLdb.connect(host=db_config.get('host', 'localhost'), port=int(db_config.get('port', 3306)), 85 | user=db_config.get('user', 'root'), passwd=db_config.get('password', ''), 86 | db=db_config.get('database', 'test'), charset=db_config.get('charset', 'utf8')) 87 | cls.conn.autocommit(cls.autocommit) 88 | cls.db_config.update(db_config) 89 | 90 | @classmethod 91 | def get_conn(cls): 92 | if not cls.conn or not cls.conn.open: 93 | cls.connect(**cls.db_config) 94 | try: 95 | cls.conn.ping() 96 | except MySQLdb.OperationalError: 97 | cls.connect(**cls.db_config) 98 | return cls.conn 99 | 100 | @classmethod 101 | def execute(cls, *args): 102 | cursor = cls.get_conn().cursor() 103 | cursor.execute(*args) 104 | return cursor 105 | 106 | def __del__(self): 107 | if self.conn and self.conn.open: 108 | self.conn.close() 109 | 110 | 111 | def execute_raw_sql(sql, params=None): 112 | return Database.execute(sql, params) if params else Database.execute(sql) 113 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | from data_handler import Database, Model, Field, execute_raw_sql 2 | 3 | # connect database 4 | db_config = { 5 | 'host': 'localhost', 6 | 'port': 3306, 7 | 'user': 'root', 8 | 'password': '123456', 9 | 'database': 'test' 10 | } 11 | Database.connect(**db_config) 12 | 13 | 14 | # define model 15 | class TestModel(Model): 16 | db_table = 'test' # point table name 17 | a = Field() 18 | b = Field() 19 | 20 | # create instance 21 | test = TestModel() 22 | test.a = 'john' 23 | test.b = 3 24 | test.save() 25 | 26 | # select 27 | for r in TestModel.where(a='john', b=3).limit(1, offset=2).select(): 28 | print type(r) 29 | print r.a 30 | print r.b 31 | 32 | # update 33 | TestModel.where(a='john', b=3).update(b=1) 34 | 35 | # execute raw sql 36 | results = execute_raw_sql('select b, count(*) from test where b = %s group by b;', (1,)) 37 | for val, cnt in results: 38 | print val, cnt --------------------------------------------------------------------------------