From ed726cd2ed094a29bf9b1038a4536e0e0efabfe3 Mon Sep 17 00:00:00 2001 From: RahulHP Date: Sat, 21 May 2016 21:53:14 +0530 Subject: [PATCH] ENH: Allow to_sql to recognize single sql type #11886 --- doc/source/whatsnew/v0.18.2.txt | 2 +- pandas/io/sql.py | 20 +++++++++++++++----- pandas/io/tests/test_sql.py | 28 ++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/doc/source/whatsnew/v0.18.2.txt b/doc/source/whatsnew/v0.18.2.txt index dfb5ebc9379b1..6dc4db97d287a 100644 --- a/doc/source/whatsnew/v0.18.2.txt +++ b/doc/source/whatsnew/v0.18.2.txt @@ -89,7 +89,7 @@ Other enhancements - ``pd.read_html()`` has gained support for the ``decimal`` option (:issue:`12907`) - +- ``DataFrame.to_sql `` now allows a single value as the SQL type for all columns (:issue:`11886`). .. _whatsnew_0182.api: diff --git a/pandas/io/sql.py b/pandas/io/sql.py index 324988360c9fe..eef1b12eb47f8 100644 --- a/pandas/io/sql.py +++ b/pandas/io/sql.py @@ -18,6 +18,7 @@ string_types, text_type) from pandas.core.api import DataFrame, Series from pandas.core.common import isnull +from pandas.core.generic import is_dictlike from pandas.core.base import PandasObject from pandas.types.api import DatetimeTZDtype from pandas.tseries.tools import to_datetime @@ -550,9 +551,10 @@ def to_sql(frame, name, con, flavor='sqlite', schema=None, if_exists='fail', chunksize : int, default None If not None, then rows will be written in batches of this size at a time. If None, all rows will be written at once. - dtype : dict of column name to SQL type, default None + dtype : single SQLtype or dict of column name to SQL type, default None Optional specifying the datatype for columns. The SQL type should be a SQLAlchemy type, or a string for sqlite3 fallback connection. + If all columns are of the same type, one single value can be used. """ if if_exists not in ('fail', 'replace', 'append'): @@ -1231,11 +1233,15 @@ def to_sql(self, frame, name, if_exists='fail', index=True, chunksize : int, default None If not None, then rows will be written in batches of this size at a time. If None, all rows will be written at once. - dtype : dict of column name to SQL type, default None + dtype : single SQL type or dict of column name to SQL type, default None Optional specifying the datatype for columns. The SQL type should - be a SQLAlchemy type. + be a SQLAlchemy type. If all columns are of the same type, one + single value can be used. """ + if dtype and not is_dictlike(dtype): + dtype = {col_name : dtype for col_name in frame} + if dtype is not None: from sqlalchemy.types import to_instance, TypeEngine for col, my_type in dtype.items(): @@ -1644,11 +1650,15 @@ def to_sql(self, frame, name, if_exists='fail', index=True, chunksize : int, default None If not None, then rows will be written in batches of this size at a time. If None, all rows will be written at once. - dtype : dict of column name to SQL type, default None + dtype : single SQL type or dict of column name to SQL type, default None Optional specifying the datatype for columns. The SQL type should - be a string. + be a string. If all columns are of the same type, one single value + can be used. """ + if dtype and not is_dictlike(dtype): + dtype = {col_name : dtype for col_name in frame} + if dtype is not None: for col, my_type in dtype.items(): if not isinstance(my_type, str): diff --git a/pandas/io/tests/test_sql.py b/pandas/io/tests/test_sql.py index 9a995c17f0445..9aa3fa7330c4b 100644 --- a/pandas/io/tests/test_sql.py +++ b/pandas/io/tests/test_sql.py @@ -1552,6 +1552,21 @@ def test_dtype(self): self.assertTrue(isinstance(sqltype, sqlalchemy.String)) self.assertEqual(sqltype.length, 10) + def test_to_sql_save_indexgle_dtype(self): + self.drop('single_dtype_test') + cols = ['A','B'] + data = [('a','b'), + ('c','d')] + df = DataFrame(data,columns=cols) + df.to_sql('single_dtype_test',self.conn,dtype=sqlalchemy.TEXT) + meta = sqlalchemy.schema.MetaData(bind=self.conn) + meta.reflect() + sqltypea = meta.tables['single_dtype_test'].columns['A'].type + sqltypeb = meta.tables['single_dtype_test'].columns['B'].type + self.assertTrue(isinstance(sqltypea, sqlalchemy.TEXT)) + self.assertTrue(isinstance(sqltypeb, sqlalchemy.TEXT)) + self.drop_table('single_dtype_test') + def test_notnull_dtype(self): cols = {'Bool': Series([True, None]), 'Date': Series([datetime(2012, 5, 1), None]), @@ -2025,6 +2040,19 @@ def test_dtype(self): self.assertRaises(ValueError, df.to_sql, 'error', self.conn, dtype={'B': bool}) + def test_to_sql_single_dtype(self): + if self.flavor == 'mysql': + raise nose.SkipTest('Not applicable to MySQL legacy') + self.drop_table('single_dtype_test') + cols = ['A','B'] + data = [('a','b'), + ('c','d')] + df = DataFrame(data,columns=cols) + df.to_sql('single_dtype_test',self.conn,dtype='STRING') + self.assertEqual(self._get_sqlite_column_type('single_dtype_test','A'),'STRING') + self.assertEqual(self._get_sqlite_column_type('single_dtype_test','B'),'STRING') + self.drop_table('single_dtype_test') + def test_notnull_dtype(self): if self.flavor == 'mysql': raise nose.SkipTest('Not applicable to MySQL legacy')