python sqlalchemy insert multiple lines in a tuple data structure
Asked Answered
T

2

5

I have been researching how to insert a list of ~500 tuples (rows) that has 7 elements (columns) into a database. I have read through various posts on stackoverflow as well as other forums. I found the following and it suggests to use the 'executemany()' method but its not so clear to me how. Do I need to covert my object from tuple to a dictionary? The problem is I don't have a name:value type of data structure.

How to use SQLAlchemy to dump an SQL file from query expressions to bulk-insert into a DBMS?

Here is an example:

engine = create_engine('sqlite:///:memory:', echo=True)
metadata = MetaData()
hockey= Table('hockey', metadata, 
    Column('team', String(16), primary_key=True),
    Column('jersey_colour', String(16)),
    Column('stadium', String(32)),
    Column('goals', Integer),
    Column('date', Date, primary_key=True),
    Column('assists', Integer))

>>>data[0]
[(u'Maple Leafs', u'Blue', u'Air Canada Center', 151, '2013-03-25', 301)]

Edit:

I tried the solution described (Sqlalchemy core, insert multiple rows from a tuple instead of dict) as follows:

markers = ','.join('?' * len(data[0]))
ins = 'INSERT INTO {tablename} VALUES ({markers})'
ins = ins.format(tablename=hockey.name, markers=markers)

>>str(ins)
'INSERT INTO hockey VALUES (?,?,?,?,?,?)'

conn = engine.connect()
result = conn.execute(ins, data)

In [59]: result = conn.execute(ins, data)
2013-03-26 07:29:28,371 INFO sqlalchemy.engine.base.Engine INSERT INTO hockey VALUES (?,?,?,?,?,?)
2013-03-26 07:29:28,371 INFO sqlalchemy.engine.base.Engine (u'Maple Leafs', u'Blue', u'Air Canada Center', 151, '2013-03-25', 301)
2013-03-26 07:29:28,371 INFO sqlalchemy.engine.base.Engine ROLLBACK
---------------------------------------------------------------------------
OperationalError                          Traceback (most recent call last)
<ipython-input-59-dafe2aef2c66> in <module>()
----> 1 result = conn.execute(ins, data)

/usr/lib/python2.7/site-packages/sqlalchemy/engine/base.pyc in execute(self, object, *multiparams, **params)
    662                                                 object,
    663                                                 multiparams,
--> 664                                                 params)
    665         else:
    666             raise exc.InvalidRequestError(

/usr/lib/python2.7/site-packages/sqlalchemy/engine/base.pyc in _execute_text(self, statement, multiparams, params)
    806             statement,
    807             parameters,
--> 808             statement, parameters
    809         )
    810         if self._has_events:

/usr/lib/python2.7/site-packages/sqlalchemy/engine/base.pyc in _execute_context(self, dialect, constructor, statement, parameters, *args)
    876                                 parameters,
    877                                 cursor,
--> 878                                 context)
    879             raise
    880

/usr/lib/python2.7/site-packages/sqlalchemy/engine/base.pyc in _execute_context(self, dialect, constructor, statement, parameters, *args)
    869                                     statement,
    870                                     parameters,
--> 871                                     context)
    872         except Exception, e:
    873             self._handle_dbapi_exception(

/usr/lib/python2.7/site-packages/sqlalchemy/engine/default.pyc in do_execute(self, cursor, statement, parameters, context)
    318
    319     def do_execute(self, cursor, statement, parameters, context=None):
--> 320         cursor.execute(statement, parameters)
    321
    322     def do_execute_no_params(self, cursor, statement, context=None):

OperationalError: (OperationalError) near "hockey": syntax error 'INSERT INTO hockey VALUES (?,?,?,?,?,?)' (u'Maple Leafs', u'Blue', u'Air Canada Center', 151, '2013-03-25', 301)

There error:

  OperationalError: (OperationalError) near "hockey": syntax error 'INSERT INTO hockey VALUES (?,?,?,?,?,?)' (u'Maple Leafs', u'Blue', u'Air Canada Center', 151, '2013-03-25', 301)
Tipton answered 26/3, 2013 at 2:10 Comment(3)
It looks like you DO have to supply a list of dictionaries according to the tutorial To issue many inserts using DBAPI’s executemany() method, we can send in a list of dictionaries each containing a distinct set of parameters to be inserted, as we do here to add some email addresses:. Just convert the tuple into dictionary.Icecap
Thanks CppLearner. So when I convert the tuple to a dict does it matter what the key field is? Does the key field have to match the column names in the table? Appreciate your help. Thank you.Tipton
yes! The name of the dictionary key should match the attribute (or field) name in the database! so use whatever you have for Column. TIcecap
T
2

Well I did the following:

column_names = tuple(c.name for c in hockey.c)

>>>column_names
('team', 'jersey_colour', 'stadium', 'goals', 'date', 'assists')

final = [dict(zip(column_names,x)) for x in data]

The above creates a list of dictionaries for each or the rows. This should work but when I run I get the following error:

>>>conn.execute(ins, final)

SQLite Date type only accepts Python date objects as input.

In any case this is another problem that I need to look into. That said, I am answering and accepting this question because the above dictionary should work.

Tipton answered 26/3, 2013 at 17:40 Comment(0)
T
1

Hello from the future!

I found myself also wanting to do bulk data insertions based on an existing SQLAlchemy table (or model, inheriting from declarative_base() in my case). I also chafed at the idea of having to create a dictionary on-the-fly just to do insertions, and went looking for a more straightforward way.

I'm sure a lot has changed since you asked/answered this question in 2013, but in 2021, with SQLAlchemy 1.4 and Python 3.7.something, here is what worked for me:

from sqlalchemy import create_engine, MetaData, Table, Column, String, Integer

engine = create_engine('sqlite:///:memory:', echo=True)
metadata = MetaData(bind=engine)

hockey = Table('hockey', metadata, 
    Column('team', String(16), primary_key=True),
    Column('jersey_colour', String(16)),
    Column('stadium', String(32)),
    Column('goals', Integer),
    Column('date', String(10), primary_key=True),
    Column('assists', Integer))

data = [
    ['Blue', 'Toronto Maple Leafs', 'Air Canada Center', '2013-03-25', 301, 151], 
    ['Red', 'Calgary Flames', 'PenWest', '2013-03-25', 254, 147]
]

metadata.create_all()
engine.execute(hockey.insert().values(data))

You might notice that I changed the date field to a string, for simplicity, since the SQLite DB-API library wants a Python datetime.date for those as noted here. (Anyone puzzled by this little sleight-of–hand should see this answer for clarification.)

Anyway, inserting a two-dimensional data structure into a SQLite database using .insert().values works fine nowadays, without the dict(zip(…)) rigamarole.

>>> from sqlalchemy import select
>>> engine.execute(select(hockey)).all()

[('Blue', 'Toronto Maple Leafs', 'Air Canada Center', '2013-03-25', '301', 151),
 ('Red', 'Calgary Flames', 'PenWest', '2013-03-25', '254', 147)]
Tavis answered 30/12, 2021 at 22:51 Comment(1)
How it can be acheived using raw sql instead of orm?Barrier

© 2022 - 2024 — McMap. All rights reserved.