Metadata-Version: 1.1
Name: dbpy
Version: 0.1.3
Summary: database abstraction layer for pythoneer
Home-page: https://github.com/whiteclover/dbpy
Author: Thomas Huang
Author-email: lyanghwy@gmail.com
License: GPL
Description: dbpy

        #####

        

        

        

        dbpy is database abstration layer wrote by python. The design is inspired by `webpy db <https://github.com/webpy/webpy>`_ and `drupal database <https://www.drupal.org/developing/api/database>`_ . If like the simple db abstration layer like ``tornado db`` or ``webpy db``, it is worth to try.

        

        

        `中文|chinese <https://github.com/thomashuang/dbpy/blob/master/README_CN.rst>`_

        

        changes

        ==========

        

        #. Add pymysql adapter

        

        

        

        Install the extension with the following command::

        

            $ easy_install pymysql

        

        or alternatively if you have pip installed::

        

        

            $ pip install pymysql

        

        Featues

        ================

        

        #. silmple and flexible

        #. graceful and useful sql query builder.

        #. thread-safe connection pool

        #. supports read/write master-slave mode

        #. supports transaction

        

        The Projects use dbpy

        ======================

        

        

        `Lilac (Distributed Scheduler Task System) <https://github.com/thomashuang/Lilac>`_

        

        .. contents::

            :depth: 4

        

        

        

        

        Install

        ==============

        

        Install the extension with the following command::

        

            $ easy_install dbpy

        

        or alternatively if you have pip installed::

        

        

            $ pip install dbpy

        

        

        or clone it form github then run the command in shell:

        

        .. code-block:: bash

        

            cd db # the path to the project

            python setup.py install

        

        Development

        ===========

        

        Fork or download it, then run:

        

        .. code-block:: bash 

        

            cd db # the path to the project

            python setup.py develop

        

        

        

        Compatibility

        =============

        

        Built and tested under Python 2.7+

        

        

        DB API

        ========

        

        

        Have a look:

        

        .. code-block:: python

        

            config = {

                    'passwd': 'test',

                    'user': 'test',

                    'host': 'localhost',

                    'db': 'test',

                    'max_idle' : 5*60

                }

        

            db.setup(config,  minconn=5, maxconn=10,  

                adapter='mysql', key='default', slave=False)

        

            db.execute('show tables')

        

        

        

        setup

        ---------

        

        :config: the connection basic config, the all of arguements of MySQLDB#connect is acceptable。 the ``max_idle`` is the connect timeout setting that is used to reconnection when connection is timeout, default is 10 seconds.

        :minconn: the minimum connections for the connection pool, default is 5.

        :maxconn: the maximum connections for the connection pool, default is 10.

        :adapter: the database driver adapter name, currently supports mysql (MySQLdb, pymysql) only.

        :key: the database idenfify for database,  default database is "default"

        :slave: if set to true, the database will be register as a slave database. make sure you setup a master firstly.

        

        

        .. code-block:: python

        

            config = {

                    'passwd': 'test',

                    'user': 'test',

                    'host': 'localhost',

                    'db': 'test',

                    'max_idle' : 5*60

                }

        

            db.setup(config, key='test')

            config['host'] = 'test.slave'

            # set a slave, and now the master can only to write

            db.setup(config, key='test', slave=True) 

        

            config['host'] = 'test.slave2'

            # add more slave for 'test'

            db.setup(config, key='test', slave=True)

        

        

            config['host'] = 'host2'

            config['db'] = 'social'

            # set another database

            db.setup(config, key='social', slave=True)

        

        query

        -------

        

        

        

        query api is used for reading database operation, like select..., show tables, if you wanna update your database please use execute api.

        

        query(sql, args=None, many=None, as_dict=False, key='default'):

        

        :sql: the raw sql

        :args: the args for sql arguement to prepare execute.

        :many: when set to a greater zero integer, it will use fetchmany then yield return a generator, otherwise a list.

        :as_dict: when set to true, query api will return the database result as dict row, otherwise tuple row.

        :key: the idenfify of database.

        

        .. code-block:: python

        

            print db.query('SELECT 1')

            # > ((1L,),)

        

            # use social db

            print db.query('SELECT 1', key='social')

            # > ((1L,),)

        

            print db.query('SELECT * FROM users WHERE uid=%s and name=%s', (1, 'user_1'))

            # > ((1L, u'user_1'),)

        

            # Wanna return dict row

            print db.query('SELECT * FROM users WHERE uid=%s and name=%s', 

                        (1, 'user_1'), as_dict=True)

            # > ({'uid': 1L, 'name': u'user_1'},)

        

            # Use fetchmany(many) then yeild, Return generator

            res = db.query('SELECT * FROM users WHERE uid=%s and name=%s', 

                            (1, 'user_1'), many=5, as_dict=True)

            print res

            print res.next()

            # > <generator object _yield at 0x7f818f4b6820>

            # > {'uid': 1L, 'name': u'user_1'}

        

        

        execute

        --------

        

        the api is used for writing database operation, like insert, update, delete.. if you wanna read query your database please use query api.

        

        execute(sql, args=None, key='default'):

        

        

        :sql: the raw sql

        :args: the args for sql arguement to prepare execute.

        :key: the idenfify of database.

        

        

        Return::

        

          it returns last_insert_id when sql is insert statement, otherwise rowcount

        

        .. code-block:: python

            

            db.execute('DROP TABLE IF EXISTS `users`')

            db.execute("""CREATE TABLE `users` (

                     `uid` int(10) unsigned NOT NULL AUTO_INCREMENT,

                    `name` varchar(20) NOT NULL,

                    PRIMARY KEY (`uid`))""")

            

            # when inset mutil-values，the api will call executemany

            db.execute('INSERT INTO users VALUES(%s, %s)', [(10, 'execute_test'), (9, 'execute_test')])

            # > 9

            db.execute('DELETE FROM users WHERE name=%s', ('execute_test',))

            # > 2

        

        

            # use social db

            db.execute('delete from events where created_at<%s', (expired, ), key='social')

            # > 10

        

        select

        -----------

        

        the api is used for select sql database query.

        

        select(table, key='default'):

        

        :table: the table name

        :key: the idenfify of database 

        

        select all

        ~~~~~~~~~~~~~~~~

        

        .. code-block:: python

        

            db.select('users')

            # > SELECT * FROM `users`

        

        specific columns

        ~~~~~~~~~~~~~~~~~

        

        .. code-block:: python

        

            db.select('users').fields('uid', 'name')

            # > SELECT `uid`, `name` FROM `users`

        

        

        execute

        ~~~~~~~~~~~~~~~~

        

        when you already build your sql, try execute api to fetch your database result.

        

        execute(many=None, as_dict=False):

        

        :many: when set to a greater zero integer, it will use fetchmany then yield return a generator, otherwise a list.

        :as_dict: when set to true, query api will return the database result as dict row, otherwise tuple row.

        

        .. code-block:: python

        

            q = db.select('users').fields('uid', 'name')

            res = q.execute()

            print res

            # > ((1L, u'user_1'), (2L, u'user_2'), (3L, u'user_3'), (4L, u'user_4'), (5L, None))

        

            res = q.execute(many=2, as_dict=True)

            print res

            print res.next()

            # > <generator object _yield at 0x7f835825e820>

            # > {'uid': 1L, 'name': u'user_1'}

        

        

        Condition

        ~~~~~~~~~~~

        

        It is time to try more complex select query.

        

        condition(field, value=None, operator=None):

        

        :field: the field of table 

        :value: the value of field, defaul is None ("field is null")

        :operator: the where operator like BETWEEN, IN, NOT IN, EXISTS, NOT EXISTS, IS NULL, IS NOT NULL, LIKE, NOT LIKE, =, <, >, >=, <=, <> and so on.

        

        

        simple 

        ^^^^^^^^^^^^^^^^

        

        .. code-block:: python

        

            db.select('users').condition('uid', 1) # condition('uid', 1, '=')

            # > SELECT * FROM `users`

            # > WHERE  `uid` = %s 

        

        

        in 

        ^^^^^^^^^^^^^^^^

        

        .. code-block:: python

        

        

            db.select('users').condition('uid', (1, 3)) # condition('uid', [1, 3]) 一样

            # > SELECT * FROM `users`

            # > WHERE  `uid` IN  (%s, %s) 

        

        between 

        ^^^^^^^^^^^^^^^^

        

        .. code-block:: python

        

            db.select('users').condition('uid', (1, 3), 'between')

            # > SELECT * FROM `users`

            # > WHERE  `uid` BETWEEN %s AND %s 

        

        

        multi condition

        ^^^^^^^^^^^^^^^^^^^^^^^^

        

        .. code-block:: python

        

            db.select('users').condition('uid', 1).condition('name', 'blabla')

            # > SELECT * FROM `users`

            # > WHERE  `uid` = %s AND `name` = %s 

        

        or condition

        ^^^^^^^^^^^^^^

        

        .. code-block:: python

        

            or_cond = db.or_().condition('uid', 1).condition('name', 'blabla')

            db.select('users').condition(or_cond).condition('uid', 1, '<>')

            # > SELECT * FROM `users`

            # > WHERE  ( `uid` = %s OR `name` = %s ) AND `uid` <> %s 

        

        

        

        order by

        ~~~~~~~~~

        

        .. code-block:: python

        

            db.select('users').order_by('name')

            # > SELECT * FROM `users`

            # > ORDER BY `name`

        

            db.select('users').order_by('name', 'DESC')

            # > SELECT * FROM `users`

            # > ORDER BY `name` DESC

        

            db.select('users').order_by('name', 'DESC').order_by('uid')

            # > SELECT * FROM `users`

            # > ORDER BY `name` DESC, `uid`

        

        

        

        distinct

        ~~~~~~~~~

        

        .. code-block:: python

        

            db.select('users').distinct().condition('uid', 1)

            # > SELECT DISTINCT * FROM `users`

            # > WHERE  `uid` = %s 

        

            db.select('users').fields('uid', 'name').distinct().condition('uid', 1)

            # > SELECT DISTINCT `uid`, `name` FROM `users`

            # > WHERE  `uid` = %s 

        

        

        group by

        ~~~~~~~~~

        

        .. code-block:: python

        

            db.select('users').group_by('name', 'uid')

            # > SELECT * FROM `users`

            # > GROUP BY `name`, `uid`

        

        

        limit and offset

        ~~~~~~~~~~~~~~~~~

        

        .. code-block:: python

        

            db.select('users').limit(2).offset(5)

            # > SELECT * FROM `users`

            # > LIMIT 2 OFFSET 5

        

        null condition

        ~~~~~~~~~~~~~~~

        

        .. code-block:: python

        

            db.select('users').is_null('name').condition('uid', 5)

            # > SELECT * FROM `users`

            # > WHERE  `name` IS NULL  AND `uid` = %s 

        

            db.select('users').is_not_null('name').condition('uid', 5)

            # > SELECT * FROM `users`

            # > WHERE  `name` IS NOT NULL  AND `uid` = %s 

        

            db.select('users').condition('name', None)

            # > SELECT * FROM `users`

            # > WHERE  `name` IS NULL  

        

        

        complex conditions

        -------------------

        

        using db.and_(), db.or_(), we can build complex where conditions:

        

        .. code-block:: python

        

            or_cond = db.or_().condition('field1', 1).condition('field2', 'blabla')

            and_cond = db.and_().condition('field3', 'what').condition('field4', 'then?')

            print db.select('table_name').condition(or_cond).condition(and_cond)

        

            # > SELECT * FROM `table_name`

            # > WHERE  ( `field1` = %s OR `field2` = %s ) AND ( `field3` = %s AND `field4` = %s ) 

        

        expr

        ------------

        

        if you wanna use the aggregate functions like sum, count, please use ``erpr`` :

        

        .. code-block:: python

        

            from  db import expr

        

            db.select('users').fields(expr('count(*)'))

            # > SELECT count(*) FROM `users`

        

            db.select('users').fields(expr('count(uid)', 'total'))

            # > SELECT count(uid) AS `total` FROM `users`

        

        

        

        insert

        -----------

        

        The ``insert`` api is used for building insert into sql statement.

        

        insert(table, key='default'):

        

        :table: the table name

        :key: the idenfify of database 

        

        .. code-block:: python

        

            q = db.insert('users').values((10, 'test_insert'))

            # > INSERT INTO `users` VALUES(%s, %s)

            print q._values

            # > [(10, 'test_insert')]

        

        

            q = db.insert('users').fields('name').values({'name': 'insert_1'}).values(('insert_2',))

            # > INSERT INTO `users` (`name`) VALUES(%s)

            print q._values

            # > [('insert_1',), ('insert_2',)]

        

        

        When you use ``execute`` api to get result, it will reutrn the ``last insert id``：

        

        .. code-block:: python

            

            

            print q.execute()

            # > 2

        

        

        

        update

        -----------

        

        The ``update`` api is used for building update sql statement.

        

        update(table, key='default'):

        

        :table: the table name

        :key: the idenfify of database 

        

        

        mset and set：

        

        :mset: the value must be dict tpye, that sets mutil-fileds at once time.

        :set(column, value): set one field one time.

        

        the where conditions please see `select`_ for more information.

        

        

        .. code-block:: python

            

            

            db.update('users').mset({'name':None, 'uid' : 12}).condition('name','user_1')

            # > UPDATE `users` SET `name` = %s, `uid` = %s WHERE  `name` = %s 

        

            q = (db.update('users').set('name', 'update_test').set('uid', 12)

                .condition('name', 'user_2').condition('uid', 2)) # .execute()

            print q.to_sql()

            # > UPDATE `users` SET `name` = %s, `uid` = %s WHERE  `name` = %s AND `uid` = %s 

          

        

        

        When you use ``execute`` api to get result, it will reutrn the ``rowcount``：

        

        

        .. code-block:: python

            

            

            print q.execute()

            # > 2

        

        limit

        ~~~~~~~~~

        

        

        

        You can use limit api to lim the quantity of update.

        

        

        .. code-block:: python

            

            db.update('users').mset({'name':None, 'uid' : 12}).condition('name','user_1').limit(5)

            # > UPDATE `users` SET `name` = %s, `uid` = %s WHERE  `name` = %s  LIMIT 5

        

        delete

        -----------

        

        

        The ``delete`` api is used for building DELETE FROM sql statement.

        

        delete(table, key='default'):

        

        :table: the table name

        :key: the idenfify of database 

        

        the where conditions please see `select`_ for more information.

        

        .. code-block:: python

            

            db.delete('users').condition('name','user_1')

            # > DELETE FROM `users` WHERE  `name` = %s 

        	

        When you use ``execute`` api to get result, it will reutrn the ``rowcount``：

        

        .. code-block:: python

            

            

            print q.execute()

            # > 2

        

        

        to_sql and str

        ---------------------

        

        you can use to_sql or __str__ method to the objects of  ``select``, ``insert``, ``update``, ``delete`` to print the sql you build.

        

        

        .. code-block:: python

            

        

            q = (db.update('users').set('name', 'update_test').set('uid', 12)

                    .condition('name', 'user_2').condition('uid', 2))

            print q.to_sql()

            print q

            # > UPDATE `users` SET `name` = %s, `uid` = %s WHERE  `name` = %s AND `uid` = %s 

        

        

        transaction

        ------------

        

        transaction(table, key='default'):

        

        :table: the table name

        :key: the idenfify of database 

        

        

        The simple transaction done all or do nothing, you cann't set savepoint. 

        

        

        

        .. code-block:: python

            

        

            # with context

            with db.transaction() as t:

                t.delete('users').condition('uid', 1).execute()

                (t.update('users').mset({'name':None, 'uid' : 12})

                    .condition('name','user_1').execute())

        

        

            # the normal way

            t = db.transaction()

            t.begin()

            t.delete('users').condition('uid', 1).execute()

            (t.update('users').mset({'name':None, 'uid' : 12})

                .condition('name','user_1').execute())

        

            #if failed will rollback

            t.commit()

        

        .. note:: when uses begin must be combine with commit，otherwise the connection will not return connection pool.suggets to use ``with context``

        

        

        simple orm

        -----------

        

        the orm demo  `samples <https://github.com/thomashuang/dbpy/blob/master/samples>`_

        

        .. code-block:: python

            

            import model

            from orm import Backend

            import db

        

            db.setup({ 'host': 'localhost', 'user': 'test', 'passwd': 'test', 'db': 'blog'})

        

        

            user = Backend('user').find_by_username('username')

            if user and user.check('password'):

                print 'auth'

        

            user = model.User('username', 'email', 'real_name', 'password', 

                    'bio', 'status', 'role')

            if Backend('user').create(user):

                print 'fine'

        

            user = Backend('user').find(12)

            user.real_name = 'blablabla....'

            if Backend('user').save(user):

                print 'user saved'

        

            if Backend('user').delete(user):

                print 'delete user failed'

        

        

            post = model.Post('title', 'slug', 'description', 'html', 'css', 'js', 

                    'category', 'status', 'comments', 'author')

            if not Backend('post').create(post):

                print 'created failed'

        

        Future

        --------

        

        

        Personal idea:

        

        #. add ``join``  for select api 

        #. add a schema class for creating or changing table.

        #. add some api for mysql individual sql like ``replace`` or ``duplicate update``

        #. improve connection pool.

        

        

        LICENSE

        =======

        

            Copyright (C) 2014-2015 Thomas Huang

        

            This program is free software: you can redistribute it and/or modify

            it under the terms of the GNU General Public License as published by

            the Free Software Foundation, version 2 of the License.

        

            This program is distributed in the hope that it will be useful,

            but WITHOUT ANY WARRANTY; without even the implied warranty of

            MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

            GNU General Public License for more details.

        

            You should have received a copy of the GNU General Public License

            along with this program.  If not, see <http://www.gnu.org/licenses/>.

        

        
Keywords: database abstraction layer for pythoneer(orm,database)
Platform: UNKNOWN
Classifier: Development Status :: 3 - Alpha
Classifier: License :: OSI Approved :: GNU Affero General Public License v3
Classifier: Natural Language :: English
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Operating System :: OS Independent
Classifier: Topic :: Database
