diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index 2f8cb346935a9..b7c4d8827ade0 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -375,6 +375,7 @@ I/O - Bug in :func:`read_csv` with multi-header input and arguments referencing column names as tuples (:issue:`42446`) - Bug in :func:`read_fwf`, where difference in lengths of ``colspecs`` and ``names`` was not raising ``ValueError`` (:issue:`40830`) - Bug in :func:`Series.to_json` and :func:`DataFrame.to_json` where some attributes were skipped when serialising plain Python objects to JSON (:issue:`42768`, :issue:`33043`) +- Column headers are dropped when constructing a :class:`DataFrame` from a sqlalchemy's ``Row`` object (:issue:`40682`) - Period diff --git a/pandas/core/dtypes/inference.py b/pandas/core/dtypes/inference.py index 1360b66e77dc0..ae961e53d8b79 100644 --- a/pandas/core/dtypes/inference.py +++ b/pandas/core/dtypes/inference.py @@ -315,7 +315,7 @@ def is_named_tuple(obj) -> bool: >>> is_named_tuple((1, 2)) False """ - return isinstance(obj, tuple) and hasattr(obj, "_fields") + return isinstance(obj, abc.Sequence) and hasattr(obj, "_fields") def is_hashable(obj) -> bool: diff --git a/pandas/tests/io/test_sql.py b/pandas/tests/io/test_sql.py index 7f73b4f12c2fb..eb3097618e158 100644 --- a/pandas/tests/io/test_sql.py +++ b/pandas/tests/io/test_sql.py @@ -2189,6 +2189,44 @@ def test_bigint_warning(self): with tm.assert_produces_warning(None): sql.read_sql_table("test_bigintwarning", self.conn) + def test_row_object_is_named_tuple(self): + # GH 40682 + # Test for the is_named_tuple() function + # Placed here due to its usage of sqlalchemy + + from sqlalchemy import ( + Column, + Integer, + String, + ) + from sqlalchemy.orm import sessionmaker + + if _gt14(): + from sqlalchemy.orm import declarative_base + else: + from sqlalchemy.ext.declarative import declarative_base + + BaseModel = declarative_base() + + class Test(BaseModel): + __tablename__ = "test_frame" + id = Column(Integer, primary_key=True) + foo = Column(String(50)) + + BaseModel.metadata.create_all(self.conn) + Session = sessionmaker(bind=self.conn) + session = Session() + + df = DataFrame({"id": [0, 1], "foo": ["hello", "world"]}) + df.to_sql("test_frame", con=self.conn, index=False, if_exists="replace") + + session.commit() + foo = session.query(Test.id, Test.foo) + df = DataFrame(foo) + session.close() + + assert list(df.columns) == ["id", "foo"] + class _TestMySQLAlchemy: """