pep8
This commit is contained in:
parent
b64837e2e2
commit
bdd937e2c9
@ -24,9 +24,12 @@ log = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class Database(object):
|
class Database(object):
|
||||||
|
"""A database object represents a SQL database with multiple tables."""
|
||||||
|
|
||||||
def __init__(self, url, schema=None, reflect_metadata=True,
|
def __init__(self, url, schema=None, reflect_metadata=True,
|
||||||
engine_kwargs=None, reflect_views=True,
|
engine_kwargs=None, reflect_views=True,
|
||||||
ensure_schema=True, row_type=row_type):
|
ensure_schema=True, row_type=row_type):
|
||||||
|
"""Configure and connect to the database."""
|
||||||
if engine_kwargs is None:
|
if engine_kwargs is None:
|
||||||
engine_kwargs = {}
|
engine_kwargs = {}
|
||||||
|
|
||||||
@ -60,14 +63,14 @@ class Database(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def executable(self):
|
def executable(self):
|
||||||
""" The current connection or engine against which statements
|
"""Connection or engine against which statements will be executed."""
|
||||||
will be executed. """
|
|
||||||
if hasattr(self.local, 'connection'):
|
if hasattr(self.local, 'connection'):
|
||||||
return self.local.connection
|
return self.local.connection
|
||||||
return self.engine
|
return self.engine
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def op(self):
|
def op(self):
|
||||||
|
"""Get an alembic operations context."""
|
||||||
ctx = MigrationContext.configure(self.engine)
|
ctx = MigrationContext.configure(self.engine)
|
||||||
return Operations(ctx)
|
return Operations(ctx)
|
||||||
|
|
||||||
@ -95,11 +98,13 @@ class Database(object):
|
|||||||
del self.local.connection
|
del self.local.connection
|
||||||
|
|
||||||
def begin(self):
|
def begin(self):
|
||||||
""" Enter a transaction explicitly. No data will be written
|
"""
|
||||||
until the transaction has been committed.
|
Enter a transaction explicitly.
|
||||||
|
|
||||||
|
No data will be written until the transaction has been committed.
|
||||||
**NOTICE:** Schema modification operations, such as the creation
|
**NOTICE:** Schema modification operations, such as the creation
|
||||||
of tables or columns will not be part of the transactional context."""
|
of tables or columns will not be part of the transactional context.
|
||||||
|
"""
|
||||||
if not hasattr(self.local, 'connection'):
|
if not hasattr(self.local, 'connection'):
|
||||||
self.local.connection = self.engine.connect()
|
self.local.connection = self.engine.connect()
|
||||||
if not hasattr(self.local, 'tx'):
|
if not hasattr(self.local, 'tx'):
|
||||||
@ -109,24 +114,32 @@ class Database(object):
|
|||||||
self.local.lock_count.append(0)
|
self.local.lock_count.append(0)
|
||||||
|
|
||||||
def commit(self):
|
def commit(self):
|
||||||
""" Commit the current transaction, making all statements executed
|
"""
|
||||||
since the transaction was begun permanent. """
|
Commit the current transaction.
|
||||||
|
|
||||||
|
Make all statements executed since the transaction was begun permanent.
|
||||||
|
"""
|
||||||
if hasattr(self.local, 'tx') and self.local.tx:
|
if hasattr(self.local, 'tx') and self.local.tx:
|
||||||
self.local.tx[-1].commit()
|
self.local.tx[-1].commit()
|
||||||
self._dispose_transaction()
|
self._dispose_transaction()
|
||||||
|
|
||||||
def rollback(self):
|
def rollback(self):
|
||||||
""" Roll back the current transaction, discarding all statements
|
"""
|
||||||
executed since the transaction was begun. """
|
Roll back the current transaction.
|
||||||
|
|
||||||
|
Discard all statements executed since the transaction was begun.
|
||||||
|
"""
|
||||||
if hasattr(self.local, 'tx') and self.local.tx:
|
if hasattr(self.local, 'tx') and self.local.tx:
|
||||||
self.local.tx[-1].rollback()
|
self.local.tx[-1].rollback()
|
||||||
self._dispose_transaction()
|
self._dispose_transaction()
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
|
"""Start a transaction."""
|
||||||
self.begin()
|
self.begin()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, error_type, error_value, traceback):
|
def __exit__(self, error_type, error_value, traceback):
|
||||||
|
"""End a transaction by committing or rolling back."""
|
||||||
if error_type is None:
|
if error_type is None:
|
||||||
try:
|
try:
|
||||||
self.commit()
|
self.commit()
|
||||||
@ -138,26 +151,27 @@ class Database(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def tables(self):
|
def tables(self):
|
||||||
"""
|
"""Get a listing of all tables that exist in the database."""
|
||||||
Get a listing of all tables that exist in the database.
|
|
||||||
"""
|
|
||||||
return list(self._tables.keys())
|
return list(self._tables.keys())
|
||||||
|
|
||||||
def __contains__(self, member):
|
def __contains__(self, member):
|
||||||
|
"""Check if the given table name exists in the database."""
|
||||||
return member in self.tables
|
return member in self.tables
|
||||||
|
|
||||||
def _valid_table_name(self, table_name):
|
def _valid_table_name(self, table_name):
|
||||||
""" Check if the table name is obviously invalid. """
|
"""Check if the table name is obviously invalid."""
|
||||||
if table_name is None or not len(table_name.strip()):
|
if table_name is None or not len(table_name.strip()):
|
||||||
raise ValueError("Invalid table name: %r" % table_name)
|
raise ValueError("Invalid table name: %r" % table_name)
|
||||||
return table_name.strip()
|
return table_name.strip()
|
||||||
|
|
||||||
def create_table(self, table_name, primary_id='id', primary_type='Integer'):
|
def create_table(self, table_name, primary_id='id', primary_type='Integer'):
|
||||||
"""
|
"""
|
||||||
Creates a new table. The new table will automatically have an `id` column
|
Create a new table.
|
||||||
unless specified via optional parameter primary_id, which will be used
|
|
||||||
as the primary key of the table. Automatic id is set to be an
|
The new table will automatically have an `id` column unless specified via
|
||||||
auto-incrementing integer, while the type of custom primary_id can be a
|
optional parameter primary_id, which will be used as the primary key of the
|
||||||
|
table. Automatic id is set to be an auto-incrementing integer, while the
|
||||||
|
type of custom primary_id can be a
|
||||||
String or an Integer as specified with primary_type flag. The default
|
String or an Integer as specified with primary_type flag. The default
|
||||||
length of String is 255. The caller can specify the length.
|
length of String is 255. The caller can specify the length.
|
||||||
The caller will be responsible for the uniqueness of manual primary_id.
|
The caller will be responsible for the uniqueness of manual primary_id.
|
||||||
@ -206,10 +220,11 @@ class Database(object):
|
|||||||
|
|
||||||
def load_table(self, table_name):
|
def load_table(self, table_name):
|
||||||
"""
|
"""
|
||||||
Loads a table. This will fail if the tables does not already
|
Load a table.
|
||||||
exist in the database. If the table exists, its columns will be
|
|
||||||
reflected and are available on the :py:class:`Table <dataset.Table>`
|
This will fail if the tables does not already exist in the database. If the
|
||||||
object.
|
table exists, its columns will be reflected and are available on the
|
||||||
|
:py:class:`Table <dataset.Table>` object.
|
||||||
|
|
||||||
Returns a :py:class:`Table <dataset.Table>` instance.
|
Returns a :py:class:`Table <dataset.Table>` instance.
|
||||||
::
|
::
|
||||||
@ -228,6 +243,7 @@ class Database(object):
|
|||||||
self._release()
|
self._release()
|
||||||
|
|
||||||
def update_table(self, table_name):
|
def update_table(self, table_name):
|
||||||
|
"""Reload a table schema from the database."""
|
||||||
table_name = self._valid_table_name(table_name)
|
table_name = self._valid_table_name(table_name)
|
||||||
self.metadata = MetaData(schema=self.schema)
|
self.metadata = MetaData(schema=self.schema)
|
||||||
self.metadata.bind = self.engine
|
self.metadata.bind = self.engine
|
||||||
@ -238,8 +254,9 @@ class Database(object):
|
|||||||
|
|
||||||
def get_table(self, table_name, primary_id='id', primary_type='Integer'):
|
def get_table(self, table_name, primary_id='id', primary_type='Integer'):
|
||||||
"""
|
"""
|
||||||
Smart wrapper around *load_table* and *create_table*. Either loads a table
|
Smart wrapper around *load_table* and *create_table*.
|
||||||
or creates it if it doesn't exist yet.
|
|
||||||
|
Either loads a table or creates it if it doesn't exist yet.
|
||||||
For short-hand to create a table with custom id and type using [], where
|
For short-hand to create a table with custom id and type using [], where
|
||||||
table_name, primary_id, and primary_type are specified as a tuple
|
table_name, primary_id, and primary_type are specified as a tuple
|
||||||
|
|
||||||
@ -263,12 +280,14 @@ class Database(object):
|
|||||||
self._release()
|
self._release()
|
||||||
|
|
||||||
def __getitem__(self, table_name):
|
def __getitem__(self, table_name):
|
||||||
|
"""Get a given table."""
|
||||||
return self.get_table(table_name)
|
return self.get_table(table_name)
|
||||||
|
|
||||||
def query(self, query, **kw):
|
def query(self, query, **kw):
|
||||||
"""
|
"""
|
||||||
Run a statement on the database directly, allowing for the
|
Run a statement on the database directly.
|
||||||
execution of arbitrary read/write queries. A query can either be
|
|
||||||
|
Allows for the execution of arbitrary read/write queries. A query can either be
|
||||||
a plain text string, or a `SQLAlchemy expression <http://docs.sqlalchemy.org/en/latest/core/tutorial.html#selecting>`_.
|
a plain text string, or a `SQLAlchemy expression <http://docs.sqlalchemy.org/en/latest/core/tutorial.html#selecting>`_.
|
||||||
If a plain string is passed in, it will be converted to an expression automatically.
|
If a plain string is passed in, it will be converted to an expression automatically.
|
||||||
|
|
||||||
@ -288,4 +307,5 @@ class Database(object):
|
|||||||
row_type=self.row_type)
|
row_type=self.row_type)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
"""Text representation contains the URL."""
|
||||||
return '<Database(%s)>' % safe_url(self.url)
|
return '<Database(%s)>' % safe_url(self.url)
|
||||||
|
|||||||
@ -14,8 +14,10 @@ log = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class Table(object):
|
class Table(object):
|
||||||
|
"""Represents a table in a database and exposes common operations."""
|
||||||
|
|
||||||
def __init__(self, database, table):
|
def __init__(self, database, table):
|
||||||
|
"""Initialise the table from database schema."""
|
||||||
self.indexes = dict((i.name, i) for i in table.indexes)
|
self.indexes = dict((i.name, i) for i in table.indexes)
|
||||||
self.database = database
|
self.database = database
|
||||||
self.table = table
|
self.table = table
|
||||||
@ -23,9 +25,7 @@ class Table(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def columns(self):
|
def columns(self):
|
||||||
"""
|
"""Get a listing of all columns that exist in the table."""
|
||||||
Get a listing of all columns that exist in the table.
|
|
||||||
"""
|
|
||||||
return list(self.table.columns.keys())
|
return list(self.table.columns.keys())
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -34,9 +34,9 @@ class Table(object):
|
|||||||
|
|
||||||
def drop(self):
|
def drop(self):
|
||||||
"""
|
"""
|
||||||
Drop the table from the database, deleting both the schema
|
Drop the table from the database.
|
||||||
and all the contents within it.
|
|
||||||
|
|
||||||
|
Delete both the schema and all the contents within it.
|
||||||
Note: the object will raise an Exception if you use it after
|
Note: the object will raise an Exception if you use it after
|
||||||
dropping the table. If you want to re-create the table, make
|
dropping the table. If you want to re-create the table, make
|
||||||
sure to get a fresh instance from the :py:class:`Database <dataset.Database>`.
|
sure to get a fresh instance from the :py:class:`Database <dataset.Database>`.
|
||||||
@ -55,6 +55,7 @@ class Table(object):
|
|||||||
def insert(self, row, ensure=None, types={}):
|
def insert(self, row, ensure=None, types={}):
|
||||||
"""
|
"""
|
||||||
Add a row (type: dict) by inserting it into the table.
|
Add a row (type: dict) by inserting it into the table.
|
||||||
|
|
||||||
If ``ensure`` is set, any of the keys of the row are not
|
If ``ensure`` is set, any of the keys of the row are not
|
||||||
table columns, they will be created automatically.
|
table columns, they will be created automatically.
|
||||||
|
|
||||||
@ -80,9 +81,11 @@ class Table(object):
|
|||||||
|
|
||||||
def insert_many(self, rows, chunk_size=1000, ensure=None, types={}):
|
def insert_many(self, rows, chunk_size=1000, ensure=None, types={}):
|
||||||
"""
|
"""
|
||||||
Add many rows at a time, which is significantly faster than adding
|
Add many rows at a time.
|
||||||
them one by one. Per default the rows are processed in chunks of
|
|
||||||
1000 per commit, unless you specify a different ``chunk_size``.
|
This is significantly faster than adding them one by one. Per default
|
||||||
|
the rows are processed in chunks of 1000 per commit, unless you specify
|
||||||
|
a different ``chunk_size``.
|
||||||
|
|
||||||
See :py:meth:`insert() <dataset.Table.insert>` for details on
|
See :py:meth:`insert() <dataset.Table.insert>` for details on
|
||||||
the other parameters.
|
the other parameters.
|
||||||
@ -112,9 +115,10 @@ class Table(object):
|
|||||||
|
|
||||||
def update(self, row, keys, ensure=None, types={}):
|
def update(self, row, keys, ensure=None, types={}):
|
||||||
"""
|
"""
|
||||||
Update a row in the table. The update is managed via
|
Update a row in the table.
|
||||||
the set of column names stated in ``keys``: they will be
|
|
||||||
used as filters for the data to be updated, using the values
|
The update is managed via the set of column names stated in ``keys``:
|
||||||
|
they will be used as filters for the data to be updated, using the values
|
||||||
in ``row``.
|
in ``row``.
|
||||||
::
|
::
|
||||||
|
|
||||||
@ -154,8 +158,10 @@ class Table(object):
|
|||||||
|
|
||||||
def upsert(self, row, keys, ensure=None, types={}):
|
def upsert(self, row, keys, ensure=None, types={}):
|
||||||
"""
|
"""
|
||||||
An UPSERT is a smart combination of insert and update. If rows with matching ``keys`` exist
|
An UPSERT is a smart combination of insert and update.
|
||||||
they will be updated, otherwise a new row is inserted in the table.
|
|
||||||
|
If rows with matching ``keys`` exist they will be updated, otherwise a
|
||||||
|
new row is inserted in the table.
|
||||||
::
|
::
|
||||||
|
|
||||||
data = dict(id=10, title='I am a banana!')
|
data = dict(id=10, title='I am a banana!')
|
||||||
@ -190,9 +196,12 @@ class Table(object):
|
|||||||
return self.insert(row, ensure=ensure, types=types)
|
return self.insert(row, ensure=ensure, types=types)
|
||||||
|
|
||||||
def delete(self, *_clauses, **_filter):
|
def delete(self, *_clauses, **_filter):
|
||||||
""" Delete rows from the table. Keyword arguments can be used
|
"""
|
||||||
to add column-based filters. The filter criterion will always
|
|
||||||
be equality:
|
Delete rows from the table.
|
||||||
|
|
||||||
|
Keyword arguments can be used to add column-based filters. The filter
|
||||||
|
criterion will always be equality:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@ -242,6 +251,7 @@ class Table(object):
|
|||||||
def create_column(self, name, type):
|
def create_column(self, name, type):
|
||||||
"""
|
"""
|
||||||
Explicitely create a new column ``name`` of a specified type.
|
Explicitely create a new column ``name`` of a specified type.
|
||||||
|
|
||||||
``type`` must be a `SQLAlchemy column type <http://docs.sqlalchemy.org/en/rel_0_8/core/types.html>`_.
|
``type`` must be a `SQLAlchemy column type <http://docs.sqlalchemy.org/en/rel_0_8/core/types.html>`_.
|
||||||
::
|
::
|
||||||
|
|
||||||
@ -263,7 +273,8 @@ class Table(object):
|
|||||||
|
|
||||||
def drop_column(self, name):
|
def drop_column(self, name):
|
||||||
"""
|
"""
|
||||||
Drop the column ``name``
|
Drop the column ``name``.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
table.drop_column('created_at')
|
table.drop_column('created_at')
|
||||||
@ -284,7 +295,9 @@ class Table(object):
|
|||||||
|
|
||||||
def create_index(self, columns, name=None):
|
def create_index(self, columns, name=None):
|
||||||
"""
|
"""
|
||||||
Create an index to speed up queries on a table. If no ``name`` is given a random name is created.
|
Create an index to speed up queries on a table.
|
||||||
|
|
||||||
|
If no ``name`` is given a random name is created.
|
||||||
::
|
::
|
||||||
|
|
||||||
table.create_index(['name', 'country'])
|
table.create_index(['name', 'country'])
|
||||||
@ -319,7 +332,10 @@ class Table(object):
|
|||||||
|
|
||||||
def find_one(self, *args, **kwargs):
|
def find_one(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
Get a single result from the table.
|
||||||
|
|
||||||
Works just like :py:meth:`find() <dataset.Table.find>` but returns one result, or None.
|
Works just like :py:meth:`find() <dataset.Table.find>` but returns one result, or None.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
row = table.find_one(country='United States')
|
row = table.find_one(country='United States')
|
||||||
@ -339,7 +355,9 @@ class Table(object):
|
|||||||
|
|
||||||
def find(self, *_clauses, **kwargs):
|
def find(self, *_clauses, **kwargs):
|
||||||
"""
|
"""
|
||||||
Performs a simple search on the table. Simply pass keyword arguments as ``filter``.
|
Perform a simple search on the table.
|
||||||
|
|
||||||
|
Simply pass keyword arguments as ``filter``.
|
||||||
::
|
::
|
||||||
|
|
||||||
results = table.find(country='France')
|
results = table.find(country='France')
|
||||||
@ -359,7 +377,8 @@ class Table(object):
|
|||||||
results = table.find(order_by=['country', '-year'])
|
results = table.find(order_by=['country', '-year'])
|
||||||
|
|
||||||
For more complex queries, please use :py:meth:`db.query() <dataset.Database.query>`
|
For more complex queries, please use :py:meth:`db.query() <dataset.Database.query>`
|
||||||
instead."""
|
instead.
|
||||||
|
"""
|
||||||
_limit = kwargs.pop('_limit', None)
|
_limit = kwargs.pop('_limit', None)
|
||||||
_offset = kwargs.pop('_offset', 0)
|
_offset = kwargs.pop('_offset', 0)
|
||||||
_step = kwargs.pop('_step', 5000)
|
_step = kwargs.pop('_step', 5000)
|
||||||
@ -398,20 +417,17 @@ class Table(object):
|
|||||||
row_type=self.database.row_type, step=_step)
|
row_type=self.database.row_type, step=_step)
|
||||||
|
|
||||||
def count(self, *args, **kwargs):
|
def count(self, *args, **kwargs):
|
||||||
"""
|
"""Return the count of results for the given filter set."""
|
||||||
Return the count of results for the given filter set (same filter options as with ``find()``).
|
|
||||||
"""
|
|
||||||
return self.find(*args, return_count=True, **kwargs)
|
return self.find(*args, return_count=True, **kwargs)
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
"""
|
"""Return the number of rows in the table."""
|
||||||
Returns the number of rows in the table.
|
|
||||||
"""
|
|
||||||
return self.count()
|
return self.count()
|
||||||
|
|
||||||
def distinct(self, *args, **_filter):
|
def distinct(self, *args, **_filter):
|
||||||
"""
|
"""
|
||||||
Returns all rows of a table, but removes rows in with duplicate values in ``columns``.
|
Return all rows of a table, but remove rows in with duplicate values in ``columns``.
|
||||||
|
|
||||||
Interally this creates a `DISTINCT statement <http://www.w3schools.com/sql/sql_distinct.asp>`_.
|
Interally this creates a `DISTINCT statement <http://www.w3schools.com/sql/sql_distinct.asp>`_.
|
||||||
::
|
::
|
||||||
|
|
||||||
@ -442,7 +458,10 @@ class Table(object):
|
|||||||
return self.database.query(q)
|
return self.database.query(q)
|
||||||
|
|
||||||
def __getitem__(self, item):
|
def __getitem__(self, item):
|
||||||
""" This is an alias for distinct which allows the table to be queried as using
|
"""
|
||||||
|
Get distinct column values.
|
||||||
|
|
||||||
|
This is an alias for distinct which allows the table to be queried as using
|
||||||
square bracket syntax.
|
square bracket syntax.
|
||||||
::
|
::
|
||||||
# Same as distinct:
|
# Same as distinct:
|
||||||
@ -454,15 +473,19 @@ class Table(object):
|
|||||||
|
|
||||||
def all(self):
|
def all(self):
|
||||||
"""
|
"""
|
||||||
Returns all rows of the table as simple dictionaries. This is simply a shortcut
|
Return all rows of the table as simple dictionaries.
|
||||||
to *find()* called with no arguments.
|
|
||||||
|
This is simply a shortcut to *find()* called with no arguments.
|
||||||
::
|
::
|
||||||
|
|
||||||
rows = table.all()"""
|
rows = table.all()
|
||||||
|
"""
|
||||||
return self.find()
|
return self.find()
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
"""
|
"""
|
||||||
|
Return all rows of the table as simple dictionaries.
|
||||||
|
|
||||||
Allows for iterating over all rows in the table without explicetly
|
Allows for iterating over all rows in the table without explicetly
|
||||||
calling :py:meth:`all() <dataset.Table.all>`.
|
calling :py:meth:`all() <dataset.Table.all>`.
|
||||||
::
|
::
|
||||||
@ -473,4 +496,5 @@ class Table(object):
|
|||||||
return self.all()
|
return self.all()
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
"""Get table representation."""
|
||||||
return '<Table(%s)>' % self.table.name
|
return '<Table(%s)>' % self.table.name
|
||||||
|
|||||||
@ -83,7 +83,7 @@ class ResultIter(object):
|
|||||||
|
|
||||||
|
|
||||||
def safe_url(url):
|
def safe_url(url):
|
||||||
""" Remove password from printed connection URLs. """
|
"""Remove password from printed connection URLs."""
|
||||||
parsed = urlparse(url)
|
parsed = urlparse(url)
|
||||||
if parsed.password is not None:
|
if parsed.password is not None:
|
||||||
pwd = ':%s@' % parsed.password
|
pwd = ':%s@' % parsed.password
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user