How to declare this column with SQLAlchemy - python

I have a MySQL table where one of the columns is a decimal. It is created with the following parameters:
`ratio` decimal(5,3) DEFAULT NULL,
So far the sample scripts I'm putting together is super dumbed down.
import sys
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
Session = sessionmaker(bind=engine)
session = Session()
Base = declarative_base()
class DailyProjections(Base):
__tablename__ = 'daily_projections'
id = Column(Integer, primary_key=True)
name = Column(String(100))
How do I declare the ratio column here?

From the docs:
The Numeric type is designed to receive data from a database type that
is explicitly known to be a decimal type (e.g. DECIMAL, NUMERIC,
others) and not a floating point type (e.g. FLOAT, REAL, others).
So you declare it like so:
ratio = Column(Numeric(5, 3), nullable=True)

Related

How to use the same code for both sqlite and postgres

My sqlalchemy code needs to support both sqlite and postgres, but right now it is not working for sqlite.
sqlalchemy.exc.StatementError: (builtins.TypeError) SQLite DateTime type only accepts Python datetime and date objects as input.
I checked Error - "SQLite DateTime type only accepts Python " "datetime and date objects as input." but making this change in my entire code base is not possible as it has more than one place where a date string is used instead of datetime
This is my code, it works for postgres engine, but it does not work for sqlite, can I modify anything other than what the above link suggests so my code runs on both sqlite and postgres
from sqlalchemy import create_engine, Column, Integer
from sqlalchemy.orm import declarative_base, Session
from sqlalchemy.types import DateTime
Base = declarative_base()
class Foo(Base):
__tablename__ = "foo"
id = Column(Integer, primary_key=True)
col = Column(DateTime)
engine = create_engine("postgresql://tony:tony#localhost:5432")
# engine = create_engine("sqlite:///db.db")
Base.metadata.create_all(engine)
with Session(engine) as session:
session.add(Foo(col='2023-01-07T11:08:31Z'))
session.commit()
The DB-API spec expects SQL DATETIME to be provided as python datetime.datetime (docs).
I believe psycopg2 offers an extenstion that will handle ISO 8601 formatted strings, but this is an extension.
If you want to be most compatible, use datetime.datetime object to pass and retrieve dates.
Also why import DateTime from sqlalchemy.types ? It's available under sqlalchemy directly.
from datetime import datetime
from sqlalchemy import Column, Integer, create_engine, DateTime
from sqlalchemy.orm import Session, declarative_base
Base = declarative_base()
class Foo(Base):
__tablename__ = "foo"
id = Column(Integer, primary_key=True)
col = Column(DateTime)
engine = create_engine("postgresql+psycopg2://postgres:postgres#localhost:5432/postgres") # OK
engine = create_engine("sqlite:///db.db") # OK
Base.metadata.create_all(engine)
with Session(engine) as session:
session.add(Foo(col=datetime.fromisoformat("2023-01-07T11:08:31Z")))
session.commit()
If you need compatibility for runtime (not tests), you can use the following TypeDecorator.
Look further into the docs if you want more than coercing bind parameters, but this will allow your to input ISO 8601 str in SQLite.
from datetime import datetime
from sqlalchemy import Column, Integer, create_engine, DateTime
from sqlalchemy.orm import Session, declarative_base
from sqlalchemy.types import TypeDecorator
Base = declarative_base()
class ISO8601DateTime(TypeDecorator):
impl = DateTime
cache_ok = True
def process_bind_param(self, value, dialect):
if dialect.name == "sqlite" and isinstance(value, str):
value = datetime.fromisoformat(value)
return value
class Foo(Base):
__tablename__ = "foo"
id = Column(Integer, primary_key=True)
col = Column(ISO8601DateTime) # NOTE: decorated type
engine = create_engine("postgresql+psycopg2://postgres:postgres#localhost:5432/postgres") # OK
engine = create_engine("sqlite:///db.db") # OK
Base.metadata.create_all(engine)
with Session(engine) as session:
session.add(Foo(col=datetime.fromisoformat("2023-01-07T11:08:31Z")))
session.add(Foo(col="2023-01-07T11:08:31Z"))
session.commit()
NB. for tests, if unit testing, mock the database adapter, if integration (and higher) testing, use the same DB as you'll use in production.

Is there a way to use SQL Tables as classes or objects in python?

I have to tie database and programming for an assignment and I have an idea for a code but need to make sure that I can use the tables I created in mySQL as my classes or objects in Python.
Example: I use SQL to create a database of houses with specific addresses and zip codes. A client says they live in zipcode x. My program should then parse through the database and return all addresses within zipcode x. Then ideally create a table in SQL with the clients results.
Not the exact assignment but it gets the basic idea across.
You're looking for an ORM. See SQLAlchemy. Example:
from sqlalchemy import Column, String, Integer, Sequence
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
create_session = sessionmaker()
Base = declarative_base()
person_autoincr_seq = Sequence('person_autoincr_seq')
class Person(Base):
__tablename__ = "person"
id = Column(
Integer,
person_autoincr_seq,
server_default=person_autoincr_seq.next_value(),
nullable = False,
primary_key = True
)
name = Column(
String,
nullable = False
)
def __init__(self, name,id=None):
if id is not None:
self.id = id
self.name = name
Using the db:
import logging as log
from contextlib import closing
engine = sqlalchemy.engine.create_engine(
"postgresql://testuser:mypassword#127.0.0.1:5432/testdb"
)
create_session.configure(bind=engine)
try:
with closing(create_session()) as db_session:
name = db_session.query(Person.name).filter_by(id=5).one()[0]
except Exception:
log.exception("Something wrong while querying db")

SQLAchemy is never able to locate specified column in row

I'm new to SQLAlchemy (but not ORM's in general) and running into an issue where I literally cannot do anything useful. All I'm able to do is hard code my query, which is what I want to do.
Here is exactly what I'm attempting to do (see Python comments to see what does/doesn't work -
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy import MetaData, Table
import pypyodbc
connection_string = 'mssql+pyodbc://U:P#SERVER/DB'
Base = declarative_base()
class Item(Base):
__tablename__ = 'Items'
id = Column('Id', Integer, primary_key=True)
engine = create_engine(connection_string, echo=True, module=pypyodbc)
sm = sessionmaker(engine)
session = sm()
# WORKS
for row in engine.execute('select top 1 Id from Items'):
print(row)
# DOES NOT WORK
print(session.query(Item).get(111))
The error I'm getting is sqlalchemy.exc.NoSuchColumnError: "Could not locate column in row for column 'Items.Id'".
The generated SQL is correct -
SELECT [Items].[Id] AS [Items_Id]
FROM [Items]
WHERE [Items].[Id] = 111
I know I'm pointing to the right database, because if I change __tablename to something non-existent I get an invalid object name error.
Why can't SQLAlchemy find my columns?

How to use postgres numeric range with SQLAlchemy

I can't happen to find much on using Postgres range types in SQLAlchemy other than this. Does anyone know how to insert new column values into a numrange column? Does anyone have some code snippets or more documentation than the one I already found?
This has been added to the official documentation: https://bitbucket.org/zzzeek/sqlalchemy/issue/3046/postgresql-range-types-need-better
Had to dig around for this, but when in doubt, check the tests! The SQLAlchemy tests for the range types use the underlying psycopg2 types.
from psycopg2.extras import NumericRange
from sqlalchemy import create_engine, Column, Integer
from sqlalchemy.dialects.postgresql import INT4RANGE
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
engine = create_engine('postgresql:///example', echo=True)
Session = sessionmaker(bind=engine)
session = Session()
Base = declarative_base(bind=engine)
class Example(Base):
__tablename__ = 'example'
id = Column(Integer, primary_key=True)
window = Column(INT4RANGE, nullable=False)
Base.metadata.create_all()
session.add(Example(window=NumericRange(2, 6)))
session.add(Example(window=NumericRange(4, 8)))
session.commit()

In Sqlalchemy, if i add an object using session.add() and flush it, session.query() does not give that object, why?

While using SQLAlchemy, i add a object to a session using session.add(objname), then either explicitly flush it using session.flush or enable autoflush=True while creating the engine itself.
Now in the session, if want to return that object via session.query(classname).all(), i cannot retrieve it.
Why is that so? or is there a way in which query() can also retrieve flushed objects.
I can't reproduce your issue. Please provide sample code that I can run to reproduce it.
Here's code that does what you describe, but behaves as one would expect:
from sqlalchemy import Column, Integer, Unicode, create_engine
from sqlalchemy.orm import create_session
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine('sqlite://')
Base = declarative_base(bind=engine)
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(Unicode(60))
Base.metadata.create_all()
After that setup, Add a new user:
s = create_session(autocommit=False, autoflush=False)
u = User()
u.name = u'Cheezo'
s.add(u)
s.flush()
Then query it back:
>>> u2 = s.query(User).first()
>>> print u2.name
Cheezo
>>> u2 is u
True

Categories

Resources