I am getting a NotImplementedError: This method is not implemented for SQLAlchemy 2.0., when trying to delete a table using the delete method in SQLAlchemy v1.4.15.
from sqlalchemy import Column, Integer, MetaData, String, Table, create_engine, delete
engine = create_engine("sqlite+pysqlite:///:memory:", echo=True, future=True)
metadata = MetaData()
user = Table(
"users", metadata, Column("id", Integer, primary_key=True), Column("name", String)
)
metadata.create_all(engine)
engine.execute(user.delete()) # leading to Traceback
# NotImplementedError: This method is not implemented for SQLAlchemy 2.0.
engine.execute is deprecated in SQLAlchemy 1.4, and will be removed in SQLAlchemy 2.0. You need to call a connection's execute method instead:
from sqlalchemy import Column, Integer, MetaData, String, Table, create_engine, delete
# Setting future=True enforces 2.0 behaviour and disables
# legacy features.
engine = create_engine("sqlite+pysqlite:///:memory:", echo=True, future=True)
metadata = MetaData()
user = Table(
"users", metadata, Column("id", Integer, primary_key=True), Column("name", String)
)
metadata.create_all(engine)
stmt = delete(user)
with engine.connect() as conn:
with conn.begin(): # Optional: start a transaction
conn.execute(stmt)
Related
I am working with SQLAlchemy and I am getting confused between Engine, Metadata, Base, and Sessions.
I did find some useful links: here, here and here, but I think it would help to know why the code below is not working.
import sqlalchemy as sqlalc
from sqlalchemy import Column, INTEGER, Integer,ForeignKey, Table, VARCHAR, TIMESTAMP, MetaData, create_engine, inspect, cast, select, SmallInteger
from sqlalchemy.orm import relationship, backref, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
url = 'mysql://info#127.0.0.1/info'
engine = create_engine(url, echo=True)
metadata = MetaData()
metadata.bind = engine
metadata.create_all(engine)
connection = engine.connect()
Base = declarative_base()
Base.metadata.create_all()
Session = sessionmaker(bind=engine)
session = Session()
When I execute, I get the following error:
sqlalchemy.exc.UnboundExecutionError: MetaData object is not bound to an Engine or Connection. Execution can not proceed without a database to execute against.
Definitions
Engine: an abstraction for a database connection, or connection pool
Metadata: an object that knows about database objects, primarily tables
Base: the base class for ORM models
Session: an object that keeps track of new, removed and changed ORM model instances while they are in use; it sends changes to the database (Session.commit()) or reverts them (Session.rollback())
Code
This code
metadata = MetaData()
metadata.bind = engine
metadata.create_all(engine)
"works" because you have bound (or associated) an engine with your MetaData instance: the engine enables connection to a database to create any tables that have been registered with the MetaData instance. However you should be aware that binding like this is deprecated in SQLAlchemy v1.4. The preferred approach is to pass the engine to create_all like this:
metadata = MetaData()
metadata.create_all(engine)
This code
Base = declarative_base()
Base.metadata.create_all()
does not work because there is no engine associated with Base.metadata. As before, the preferred approach is to pass an engine to create_all:
Base.metadata.create_all(engine)
In both cases, executing create_all will do nothing unless tables have been registered with the metadata instances. For core, this usually happens through table definitions:
metadata = MetaData()
t = Table(
't',
metadata, # <- this will associate the table with the metadata
Column(...),
)
In the ORM, (using declarative mapping), the association is made by inheriting from Base:
Base = orm.declarative_base()
class MyModel(Base): # <- this will register mytable with Base's metadata
__tablename__ = 'mytable'
...
This question already has answers here:
Sqlite / SQLAlchemy: how to enforce Foreign Keys?
(9 answers)
Closed 5 years ago.
In the below code why is there no error when inserting into table t1? Column b in t1 is a foreign key, so it should only accept values from column c in t2, but somehow I can insert 'bar' without an error. What am I missing here?
from sqlalchemy import create_engine, MetaData, Table, Column, Unicode, ForeignKey
engine = create_engine(r'sqlite:///XXXXXXX.db', echo=True)
metadata = MetaData()
t1 = Table('t1', metadata,
Column('a', Unicode(), primary_key=True),
Column('b', Unicode(), ForeignKey('t2.c')))
t2 = Table('t2', metadata,
Column('c', Unicode(), primary_key=True))
metadata.create_all(engine)
conn = engine.connect()
conn.execute(t2.insert().values(c='first'))
conn.execute(t2.insert().values(c='second'))
conn.execute(t1.insert().values(a='foo', b='bar'))
EDIT
I don't think this question should be marked as a duplicate of the linked one. The linked question is about how to enforce Foreign Key assuming you already know it is not on by default. In my question I observe a strange behaviour (violation of Foreign Key constraint) and ask for a root cause.
Thanks Ilja for a push in the right direction. I modified the code in the following way and now it's working as expected, that is raising IntegrityError on the last line.
from sqlalchemy import create_engine, MetaData, Table, Column, Unicode, ForeignKey
from sqlalchemy.engine import Engine
from sqlalchemy import event
#event.listens_for(Engine, "connect")
def set_sqlite_pragma(dbapi_connection, connection_record):
cursor = dbapi_connection.cursor()
cursor.execute("PRAGMA foreign_keys=ON")
cursor.close()
engine = create_engine(r'sqlite:///XXXXXXX.db', echo=True)
metadata = MetaData()
t1 = Table('t1', metadata,
Column('a', Unicode(), primary_key=True),
Column('b', Unicode(), ForeignKey('t2.c')))
t2 = Table('t2', metadata,
Column('c', Unicode(), primary_key=True))
metadata.create_all(engine)
conn = engine.connect()
conn.execute(t2.insert().values(c='first'))
conn.execute(t2.insert().values(c='second'))
conn.execute(t1.insert().values(a='foo', b='bar'))
I'm trying to migrate a table with SQLAlchemy Migrate, but I'm getting this error:
sqlalchemy.exc.UnboundExecutionError: Table object 'responsibles' is not bound to an Engine or Connection. Execution can not proceed without a database to execute against.
When I run:
python manage.py test
This is my migration file:
from sqlalchemy import *
from migrate import *
meta = MetaData()
responsibles = Table(
'responsibles', meta,
Column('id', Integer, primary_key=True),
Column('breakdown_type', String(255)),
Column('breakdown_name', String(500)),
Column('email', String(255)),
Column('name', String(255)),
)
def upgrade(migrate_engine):
# Upgrade operations go here. Don't create your own engine; bind
# migrate_engine to your metadata
responsibles.create()
def downgrade(migrate_engine):
# Operations to reverse the above upgrade go here.
responsibles.drop()
did you create your engine? like this
engine = create_engine('sqlite:///:memory:')
and then do
meta.bind = engine
meta.create_all(engine)
You need to supply engine or connection
sqlalchemy.schema.MetaData.bind
For e.g.:
engine = create_engine("someurl://")
metadata.bind = engine
I created the table on the server using SQLALchemy:
from sqlalchemy import create_engine, Table, Column, String, MetaData
engine = create_engine('mssql://server/database?driver=SQL+Server&trusted_connection=yes')
meta = MetaData()
table = Table('test17', meta,
Column('id', Integer, primary_key=True),
Column('name', String('255'))
)
metadata.create_all(engine)
Then I connected to this database using SSMS 2012 and added a new column:
ALTER TABLE test17 ADD age INT NULL
How do I tell using SQLALchemy that a new column appears in the table?
I tried to do something like:
meta2 = MetaData()
table = Table('test17', meta, autoload=True, autoload_with=engine)
But in the end I get the same table structure that I defined initially using SQLALchemy.
I think you forgot to bind your MetaData to the engine. Create the engine first, then read the metadata from the db using the engine.
metadata = db.MetaData(bind=engine)
test17 = db.Table('test17', metadata, autoload=True)
I have a table that already exists:
USERS_TABLE = Table("users", META_DATA,
Column("id", Integer, Sequence("user_id_seq"), primary_key=True),
Column("first_name", String(255)),
Column("last_name", String(255))
)
I created this table by running this:
CONN = create_engine(DB_URL, client_encoding="UTF-8")
META_DATA = MetaData(bind=CONN, reflect=True)
# ... table code
META_DATA.create_all(CONN, checkfirst=True)
the first time it worked and I was able to create the table. However, the 2nd time around I got this error:
sqlalchemy.exc.InvalidRequestError: Table 'users' is already defined for this MetaData instance. Specify 'extend_existing=True' to redefine options and columns on an existing Table object.
which makes sense since the table users already exists. I'm able to see if the table exists like so:
TABLE_EXISTS = CONN.dialect.has_table(CONN, "users")
However, how do I actually get the existing table object? I can't find this anywhere in the documentation. Please help.
We have 3 different approaches here:
assume that required tables have been created already, reflecting them and getting with MetaData.tables dictionary field like
from sqlalchemy import MetaData, create_engine
CONN = create_engine(DB_URL, client_encoding="UTF-8")
META_DATA = MetaData(bind=CONN, reflect=True)
USERS_TABLE = META_DATA.tables['users']
removing reflect flag from MetaData object initialization, because we don't use it and moreover – trying to create tables that've been already reflected:
from sqlalchemy import (MetaData, Table, Column, Integer, String, Sequence,
create_engine)
CONN = create_engine('sqlite:///db.sql')
META_DATA = MetaData(bind=CONN)
USERS_TABLE = Table("users", META_DATA,
Column("id", Integer, Sequence("user_id_seq"),
primary_key=True),
Column("first_name", String(255)),
Column("last_name", String(255)))
META_DATA.create_all(CONN, checkfirst=True)
assuming that we are keeping reflected table if it was previously created by setting in Table object initializer keep_existing flag to True:
from sqlalchemy import (MetaData, Table, Column, Integer, String, Sequence,
create_engine)
CONN = create_engine('sqlite:///db.sql')
META_DATA = MetaData(bind=CONN, reflect=True)
USERS_TABLE = Table("users", META_DATA,
Column("id", Integer, Sequence("user_id_seq"),
primary_key=True),
Column("first_name", String(255)),
Column("last_name", String(255)),
keep_existing=True)
META_DATA.create_all(CONN, checkfirst=True)
Which one to choose? Depends on your use case, but I prefer second one since it looks like you aren't using reflection, also it is simplest modification: just removing flag from MetaData initializer.
P. S.
we can always make reflection after initialization of MetaData object with MetaData.reflect method:
META_DATA.reflect()
also we can specify which tables to reflect with only parameter (may be any iterable of str objects):
META_DATA.reflect(only=['users'])
and many more.
This works for me pretty well -
import sqlalchemy as db
engine = db.create_engine("your_connection_string")
meta_data = db.MetaData(bind=engine)
db.MetaData.reflect(meta_data)
USERS = meta_data.tables['users']
# View the columns present in the users table
print(USERS.columns)
# You can run sqlalchemy queries
query = db.select([
USERS.c.id,
USERS.c.first_name,
USERS.c.last_name,
])
result = engine.execute(query).fetchall()
Note that using reflect parameter in Metadata(bind=engine, reflect=True) is deprecated and will be removed in a future release. Above code takes care of it.
__table_args__ = {'extend_existing': True}
right below __tablename__
If you're using async Sqlalchemy, you can use
metadata = MetaData()
async with engine.connect() as conn:
await conn.run_sync(metadata.reflect, only=["harshit_table"])
harshit_table = Table("harshit_table", metadata, autoload_with=engine)
print("tables: ", harshit_table, type(harshit_table))
I'm quite new to this, but what worked for me was this (variables are declared in the original question)
USERS_TABLE_NEW = Table("users", META_DATA, autoload_with=CONN)