Skip to content

Commit e15f56a

Browse files
author
Thomas Grainger
committed
Add documentation and tests for SQLAlchemy connectables
1 parent b2f1d8b commit e15f56a

File tree

4 files changed

+54
-23
lines changed

4 files changed

+54
-23
lines changed

doc/source/io.rst

+8-1
Original file line numberDiff line numberDiff line change
@@ -3513,9 +3513,16 @@ below and the SQLAlchemy `documentation <http://docs.sqlalchemy.org/en/rel_0_9/c
35133513
.. ipython:: python
35143514
35153515
from sqlalchemy import create_engine
3516-
# Create your connection.
3516+
# Create your engine.
35173517
engine = create_engine('sqlite:///:memory:')
35183518
3519+
If you want to manage your own connections you can pass one of those instead:
3520+
3521+
.. ipython:: python
3522+
3523+
with engine.connect() as conn, conn.begin():
3524+
data = pd.read_sql_table('data', conn)
3525+
35193526
Writing DataFrames
35203527
~~~~~~~~~~~~~~~~~~
35213528

doc/source/whatsnew/v0.17.0.txt

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ Check the :ref:`API Changes <whatsnew_0170.api>` and :ref:`deprecations <whatsne
2121
New features
2222
~~~~~~~~~~~~
2323

24+
- SQL io functions now accept a SQLAlchemy connectable. (:issue:`7877`)
25+
2426
.. _whatsnew_0170.enhancements.other:
2527

2628
Other enhancements

pandas/io/sql.py

+22-22
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ def execute(sql, con, cur=None, params=None):
139139
----------
140140
sql : string
141141
Query to be executed
142-
con : SQLAlchemy engine or sqlite3 DBAPI2 connection
142+
con : SQLAlchemy connectable or sqlite3 DBAPI2 connection
143143
Using SQLAlchemy makes it possible to use any DB supported by that
144144
library.
145145
If a DBAPI2 object, only sqlite3 is supported.
@@ -282,14 +282,14 @@ def read_sql_table(table_name, con, schema=None, index_col=None,
282282
chunksize=None):
283283
"""Read SQL database table into a DataFrame.
284284
285-
Given a table name and an SQLAlchemy engine, returns a DataFrame.
285+
Given a table name and an SQLAlchemy connectable, returns a DataFrame.
286286
This function does not support DBAPI connections.
287287
288288
Parameters
289289
----------
290290
table_name : string
291291
Name of SQL table in database
292-
con : SQLAlchemy engine
292+
con : SQLAlchemy connectable
293293
Sqlite DBAPI connection mode not supported
294294
schema : string, default None
295295
Name of SQL schema in database to query (if database flavor
@@ -330,7 +330,7 @@ def read_sql_table(table_name, con, schema=None, index_col=None,
330330
"""
331331
if not _is_sqlalchemy_connectable(con):
332332
raise NotImplementedError("read_sql_table only supported for "
333-
"SQLAlchemy engines.")
333+
"SQLAlchemy connectable.")
334334
import sqlalchemy
335335
from sqlalchemy.schema import MetaData
336336
meta = MetaData(con, schema=schema)
@@ -362,7 +362,7 @@ def read_sql_query(sql, con, index_col=None, coerce_float=True, params=None,
362362
----------
363363
sql : string
364364
SQL query to be executed
365-
con : SQLAlchemy engine or sqlite3 DBAPI2 connection
365+
con : SQLAlchemy connectable or sqlite3 DBAPI2 connection
366366
Using SQLAlchemy makes it possible to use any DB supported by that
367367
library.
368368
If a DBAPI2 object, only sqlite3 is supported.
@@ -420,7 +420,7 @@ def read_sql(sql, con, index_col=None, coerce_float=True, params=None,
420420
----------
421421
sql : string
422422
SQL query to be executed or database table name.
423-
con : SQLAlchemy engine or DBAPI2 connection (fallback mode)
423+
con : SQLAlchemy connectable or DBAPI2 connection (fallback mode)
424424
Using SQLAlchemy makes it possible to use any DB supported by that
425425
library.
426426
If a DBAPI2 object, only sqlite3 is supported.
@@ -504,14 +504,14 @@ def to_sql(frame, name, con, flavor='sqlite', schema=None, if_exists='fail',
504504
frame : DataFrame
505505
name : string
506506
Name of SQL table
507-
con : SQLAlchemy engine or sqlite3 DBAPI2 connection
507+
con : SQLAlchemy connectable or sqlite3 DBAPI2 connection
508508
Using SQLAlchemy makes it possible to use any DB supported by that
509509
library.
510510
If a DBAPI2 object, only sqlite3 is supported.
511511
flavor : {'sqlite', 'mysql'}, default 'sqlite'
512-
The flavor of SQL to use. Ignored when using SQLAlchemy engine.
512+
The flavor of SQL to use. Ignored when using SQLAlchemy connectable.
513513
'mysql' is deprecated and will be removed in future versions, but it
514-
will be further supported through SQLAlchemy engines.
514+
will be further supported through SQLAlchemy connectables.
515515
schema : string, default None
516516
Name of SQL schema in database to write to (if database flavor
517517
supports this). If None, use default schema (default).
@@ -557,14 +557,14 @@ def has_table(table_name, con, flavor='sqlite', schema=None):
557557
----------
558558
table_name: string
559559
Name of SQL table
560-
con: SQLAlchemy engine or sqlite3 DBAPI2 connection
560+
con: SQLAlchemy connectable or sqlite3 DBAPI2 connection
561561
Using SQLAlchemy makes it possible to use any DB supported by that
562562
library.
563563
If a DBAPI2 object, only sqlite3 is supported.
564564
flavor: {'sqlite', 'mysql'}, default 'sqlite'
565-
The flavor of SQL to use. Ignored when using SQLAlchemy engine.
565+
The flavor of SQL to use. Ignored when using SQLAlchemy connectable.
566566
'mysql' is deprecated and will be removed in future versions, but it
567-
will be further supported through SQLAlchemy engines.
567+
will be further supported through SQLAlchemy connectables.
568568
schema : string, default None
569569
Name of SQL schema in database to write to (if database flavor supports
570570
this). If None, use default schema (default).
@@ -581,7 +581,7 @@ def has_table(table_name, con, flavor='sqlite', schema=None):
581581

582582
_MYSQL_WARNING = ("The 'mysql' flavor with DBAPI connection is deprecated "
583583
"and will be removed in future versions. "
584-
"MySQL will be further supported with SQLAlchemy engines.")
584+
"MySQL will be further supported with SQLAlchemy connectables.")
585585

586586

587587
def pandasSQL_builder(con, flavor=None, schema=None, meta=None,
@@ -979,11 +979,11 @@ class PandasSQL(PandasObject):
979979
"""
980980

981981
def read_sql(self, *args, **kwargs):
982-
raise ValueError("PandasSQL must be created with an SQLAlchemy engine"
982+
raise ValueError("PandasSQL must be created with an SQLAlchemy connectable"
983983
" or connection+sql flavor")
984984

985985
def to_sql(self, *args, **kwargs):
986-
raise ValueError("PandasSQL must be created with an SQLAlchemy engine"
986+
raise ValueError("PandasSQL must be created with an SQLAlchemy connectable"
987987
" or connection+sql flavor")
988988

989989

@@ -994,8 +994,8 @@ class SQLDatabase(PandasSQL):
994994
995995
Parameters
996996
----------
997-
engine : SQLAlchemy Connectable (Engine or Connection)
998-
Engine to connect with the database. Using SQLAlchemy makes it
997+
engine : SQLAlchemy connectable
998+
Connectable to connect with the database. Using SQLAlchemy makes it
999999
possible to use any DB supported by that library.
10001000
schema : string, default None
10011001
Name of SQL schema in database to write to (if database flavor
@@ -1024,7 +1024,7 @@ def run_transaction(self):
10241024
yield self.connectable
10251025

10261026
def execute(self, *args, **kwargs):
1027-
"""Simple passthrough to SQLAlchemy engine"""
1027+
"""Simple passthrough to SQLAlchemy connectable"""
10281028
return self.connectable.execute(*args, **kwargs)
10291029

10301030
def read_table(self, table_name, index_col=None, coerce_float=True,
@@ -1620,12 +1620,12 @@ def get_schema(frame, name, flavor='sqlite', keys=None, con=None, dtype=None):
16201620
name : string
16211621
name of SQL table
16221622
flavor : {'sqlite', 'mysql'}, default 'sqlite'
1623-
The flavor of SQL to use. Ignored when using SQLAlchemy engine.
1623+
The flavor of SQL to use. Ignored when using SQLAlchemy connectable.
16241624
'mysql' is deprecated and will be removed in future versions, but it
16251625
will be further supported through SQLAlchemy engines.
16261626
keys : string or sequence
16271627
columns to use a primary key
1628-
con: an open SQL database connection object or an SQLAlchemy engine
1628+
con: an open SQL database connection object or a SQLAlchemy connectable
16291629
Using SQLAlchemy makes it possible to use any DB supported by that
16301630
library.
16311631
If a DBAPI2 object, only sqlite3 is supported.
@@ -1683,8 +1683,8 @@ def write_frame(frame, name, con, flavor='sqlite', if_exists='fail', **kwargs):
16831683
16841684
- With ``to_sql`` the index is written to the sql database by default. To
16851685
keep the behaviour this function you need to specify ``index=False``.
1686-
- The new ``to_sql`` function supports sqlalchemy engines to work with
1687-
different sql flavors.
1686+
- The new ``to_sql`` function supports sqlalchemy connectables to work
1687+
with different sql flavors.
16881688
16891689
See also
16901690
--------

pandas/io/tests/test_sql.py

+22
Original file line numberDiff line numberDiff line change
@@ -1369,6 +1369,28 @@ def test_double_precision(self):
13691369
self.assertTrue(isinstance(col_dict['i32'].type, sqltypes.Integer))
13701370
self.assertTrue(isinstance(col_dict['i64'].type, sqltypes.BigInteger))
13711371

1372+
def test_connectable_issue_example(self):
1373+
"""
1374+
This tests the example raised in issue
1375+
https://github.com/pydata/pandas/issues/10104
1376+
"""
1377+
1378+
def foo(connection):
1379+
query = 'SELECT test_foo_data FROM test_foo_data'
1380+
return sql.read_sql_query(query, con=connection)
1381+
1382+
def bar(connection, data):
1383+
data.to_sql(name='test_foo_data', con=connection, if_exists='append')
1384+
1385+
def main(connectable):
1386+
with connectable.connect() as conn:
1387+
with conn.begin():
1388+
foo_data = conn.run_callable(foo)
1389+
conn.run_callable(bar, foo_data)
1390+
1391+
DataFrame({'test_foo_data': [0, 1, 2]}).to_sql('test_foo_data', self.conn)
1392+
main(self.conn)
1393+
13721394

13731395
class _TestSQLAlchemyConn(_EngineToConnMixin, _TestSQLAlchemy):
13741396
def test_transactions(self):

0 commit comments

Comments
 (0)