SQLalchemy cannot find my table - python

I am trying to follow the official SQLalchemy tutorial, but less than half way through it blows up in my face. I am using the following code:
from sqlalchemy import create_engine
engine = create_engine('sqlite:///:memory:', echo=True)
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=engine, autoflush=False)
session = Session()
from sqlalchemy import Column, Integer, String, Sequence
class Substance(Base):
__tablename__ = 'substances'
id = Column(Integer, Sequence("substance_id_seq"), primary_key=True)
name = Column(String)
fullname = Column(String)
hazards = Column(String)
def __repr__(self):
return "<Substance(name='%s', fullname='%s', hazards='%s')>" % (self.name, self.fullname, self.hazards)
edta = Substance(name="EDTA", fullname="Ethylenediaminetetraacetic acid", hazards="lala")
session.add(edta)
subs = session.query(Substance).filter_by(name='EDTA').first()
print subs
This fails with:
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: substances [SQL: u'SELECT substances.id AS substances_id, substances.name AS substances_name, substances.fullname AS substances_fullname, substances.hazards AS substances_hazards \nFROM substances \nWHERE substances.name = ?\n LIMIT ? OFFSET ?'] [parameters: ('EDTA', 1, 0)]
Any ideas why or what I can do about it?

Please add Base.metadata.create_all(engine) to your code after all the classes are declared.
This creates all the tables you have declared through objects.
Also, another thing, use session.commit() to commit all the data you added through session.add(obj)

Related

SQLAlchemy ORM autoloading with relationships

I have made a table from one service (not using sqlalchemy), and now i want to query the tables using sqlalchemy
i used a syntax like this
from app import db, engine, Base
from sqlalchemy import Table, Column, String, Integer, TIMESTAMP, Float
class Post(Base):
__table__ = Table('posts', Base.metadata,
autoload=True, autoload_with=engine)
And this is working, i can query my objects.
The annoyance is, that i can't get the properties before runtime, because (obviously), there is nothing before it compiles. Is there a way to add the types when using something like this, or do i have to assume, that they are there?
For existing tables we can reflect the table-specific information (columns, FKs, etc.) and still add our own extra ORM-specific attributes like relationships:
import sqlalchemy as sa
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
print(sa.__version__) # 1.3.20
connection_uri = "mssql+pyodbc://#mssqlLocal64"
engine = sa.create_engine(connection_uri, echo=False)
# show existing sample data
with engine.connect() as conn:
results = conn.execute(sa.text("SELECT * FROM users"))
print([dict(r) for r in results])
# [{'id': 1, 'name': 'Gord'}]
results = conn.execute(sa.text("SELECT * FROM posts"))
print([dict(r) for r in results])
# [{'id': 1, 'user_id': 1, 'post': 'Hello world!'}]
Base = declarative_base()
meta = Base.metadata
class User(Base):
__table__ = sa.Table("users", meta, autoload_with=engine)
class Post(Base):
__table__ = sa.Table("posts", meta, autoload_with=engine)
author = sa.orm.relationship("User")
def __repr__(self):
return f"<Post(id={self.id}, author_name='{self.author.name}', post='{self.post}')>"
Session = sessionmaker(bind=engine)
session = Session()
my_post = session.query(Post).get(1)
print(my_post) # <Post(id=1, author_name='Gord', post='Hello world!')>

with_entities referring to SQlite column aliased with <label> (self-contained repost)

This is an edited version of the original question, with a self-contained example.
from sqlalchemy import Column, Integer, String, create_engine, func
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
fullname = Column(String)
nickname = Column(String)
engine = create_engine('sqlite://', echo=True)
Session = sessionmaker(bind=engine)
session = Session()
Base.metadata.create_all(engine)
session.add_all([
User(name='wendy', fullname='Wendy Williams'),
User(name='mary', fullname='Mary Contrary'),
User(name='fred', fullname='Fred Flintstone', nickname='freddy')])
session.commit()
items = session.query(
User.id, User.name,
func.coalesce(User.nickname, 'Nicky').label('nickname'))
# Checking that with_entities works on original data set
subset_items = session.query(
User.id, User.name)\
.with_entities(User.name, User.nickname)\
.all()
print(subset_items)
# items will be used elsewhere....
# Wanted: a subset with columns fullname and nickname only
# Result is wrong, it does not use coalesce result
special_items = items\
.with_entities(User.fullname, User.nickname) \
.all()
print(special_items)
How do I use .with_entities to refer to the items? The following fails
.with_entities(items.fullname, items.nickname) \
so did variants with quotes as suggested by #Nick in reply to the original question.
I haven't tested this, but I believe you should be able to use a string here instead of an expression:
.with_entities('pat_no') \
Since there was no reply, I posted this on the community site of SQLAlchemy. The answer is surprisingly awkward:
q = q.with_entities(q.column_descriptions[1]['expr'], q.column_descriptions[2]['expr'])

Creating schema via declarative mapping: Base.metadata.create_all(engine) does not work

Here is an absurd problem with sqlalchemy that seems easy! First, this is my config file for connecting to mysql database:
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
engine = create_engine('mysql://root:#localhost:3306/digi')
and then, I am trying to create a table called 'sale-history' :
from config import *
from sqlalchemy import *
class Sale(Base):
__tablename__ = 'sale-history'
order_id = column(Integer, primary_key= True)
customer_id = column(Integer)
item_id = column(Integer) #froeign key with product list
cartFinalize_dateTime = column(DATETIME)
amount_ordrered = column(Integer)
city_name = column(String(191))
quantity_ordered = column(Integer)
def __repr__(self):
return "<Sale(city_name='%s')>" % (self.city_name)
Sale.__table__
Base.metadata.create_all(engine)
Now, what I wonder is that
Sale.__table__
and
Base.metadata.create_all(engine)
are not known to my code. More accurate, these are not in suggestion options showed by pycharm editor. Debugging the code does not throw any error(returns 0). What should I do to create tables?
I appreciate your consideration so much!
The code is using column to define columns in the table but it should be using Column (note the upper-case "C").
A few tips/comments
Pycharm may provide better support if you avoid the from module import * idiom. You can alias module names if they are to long to type, for example import sqlalchemy as sa
You can see the SQL generated by the engine by passing echo=True to create_engine
Tablenames with hyphens need to be quoted with backticks to be valid. Sqlalchemy does this automatically, but other applications may not. Using underscores instead may be more convenient.
The final code might look like this:
config
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
engine = create_engine('mysql://root:#localhost:3306/test', echo=True)
Model
import sqlachemy as sa
import config
class Sale(Base):
__tablename__ = 'sale-history'
order_id = sa.Column(sa.Integer, primary_key=True)
customer_id = sa.Column(sa.Integer)
item_id = sa.Column(sa.Integer) # foreign key with product list
cartFinalize_dateTime = sa.Column(sa.DATETIME)
amount_ordrered = sa.Column(sa.Integer)
city_name = sa.Column(sa.String(191))
quantity_ordered = sa.Column(sa.Integer)
def __repr__(self):
return "<Sale(city_name='%s')>" % (self.city_name)
Base.metadata.create_all(config.engine)

Get last inserted record's Primary Key in "declarative_base()"

I wanna to get Primary Key of last inserted, I already know two way for this :
1) "lastrowid" with "raw SQL"
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, text
engine = create_engine('sqlite://')
meta = MetaData()
tbl = Table('tbl', meta,
Column('f1', Integer, primary_key=True),
Column('f2', String(64))
)
tbl.create(engine)
sql = text("INSERT INTO tbl VALUES (NULL, 'some_data')")
res = engine.execute(sql)
print(res.lastrowid)
2) "inserted_primary_key" with "insert()"
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String
engine = create_engine('sqlite://')
meta = MetaData()
tbl = Table('tbl', meta,
Column('f1', Integer, primary_key=True),
Column('f2', String(64))
)
tbl.create(engine)
ins = tbl.insert().values(f2='some_data')
res = engine.execute(ins)
print(res.inserted_primary_key)
but my problem is "declarative_base()"
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
engine = create_engine('sqlite://')
Base = declarative_base()
Session = sessionmaker(bind=engine)
session = Session()
class TBL(Base):
__tablename__ = 'tbl'
f1 = Column(Integer, primary_key=True)
f2 = Column(String(64))
Base.metadata.create_all(engine)
rcd = TBL(f2='some_data')
session.add(rcd)
session.commit()
If i do this:
res = session.add(rcd)
It give me "None". or if i do this:
res = session.commit()
same thing happend. My question is:
Is there any good way to access "lastrowid" or "inserted_primary_key" in case of "declarative_base()"?
What is the best approach ?
After calling session.commit(), accessing rcd.f1 will return its generated primary key. SQLAlchemy automatically reloads the object from database after it has been expired by the commit.

Using a variable on sqlalchemy query filter

its possible to use variables on filters?
My actual code:
import sqlalchemy
from sqlalchemy import Table, MetaData, Column, Integer, String
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
location = 'localhost'
engine_str = 'mysql+mysqlconnector://xxx:xxx#xxx/xxx'.format(location)
engine = sqlalchemy.create_engine(engine_str, echo=False)
session = sessionmaker(bind=engine)
connection = engine.connect()
session = session(bind=connection)
metadata = MetaData()
Base = declarative_base()
class Services(Base):
"""
Service details on database
"""
__tablename__ = 'some_table'
id = Column(Integer, primary_key=True)
color= Column(String)
shape= Column(String)
def __repr__(self):
return self.shape
This works:
for c in session.query(Services).filter(Services.name.in_(['blue','red'])):
print(c)
result:
circle, square
But using avariable, returns:
services = ('blue','red')
for c in session.query(Services).filter(Services.name.in_([services])):
print(c)
result:
mysql.connector.errors.ProgrammingError: Failed processing pyformat-parameters; 'MySQLConverter' object has no attribute '_tuple_to_mysql'
Thanks in advance!
services is already a sequence, no need to wrap it in a list:
for c in session.query(Services).filter(Services.name.in_(services)):

Categories

Resources