How to create a single table using SqlAlchemy declarative_base - python

To create the User table I have to use drop_all and then create_all methods. But these two functions re-initiate an entire database. I wonder if there is a way to create the User table without erasing (or dropping) any existing tables in a database?
import sqlalchemy
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True)
name = sqlalchemy.Column(sqlalchemy.String)
def __init__(self, code=None, *args, **kwargs):
self.name = name
url = 'postgresql+psycopg2://user:pass#01.02.03.04/my_db'
engine = sqlalchemy.create_engine(url)
session = sqlalchemy.orm.scoped_session(sqlalchemy.orm.sessionmaker())
session.configure(bind=engine, autoflush=False, expire_on_commit=False)
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)

You can create/drop individual tables:
User.__table__.drop(engine)
User.__table__.create(engine)

from app import db
from models import User
User.__table__.create(db.engine)
User.__table__.drop(db.engine)

Another way to accomplish the task:
Base.metadata.tables['users'].create(engine)
Base.metadata.tables['users'].drop(engine)

Related

SQLAlchemy: Unnecessary join with joinedload

I'm using SQLAlchemy ORM for a Flask project where I want to join across an eagerly loaded model but this leads to two joins to the same intermediary model. If you run the code below you'll see in the generated SQL that there are two joins between the Author model and the Book model. If the lazy=joined bit is removed the sql generated is perfect.
I don't know if I'm doing something wrong or this is by design. How do I get SQLAlchemy to emit the right SQL while maintaining the joinedload in this case?
Note: I have tried this with MySQL and SQLite and it happens with both those dbs.
from sqlalchemy import create_engine, Integer, String, Column
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship
engine = create_engine('sqlite:///:memory:', echo=True)
Session = sessionmaker(bind=engine)
Base = declarative_base()
from sqlalchemy import create_engine, Integer, String, Column
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship
engine = create_engine('sqlite:///:memory:', echo=True)
Session = sessionmaker(bind=engine)
Base = declarative_base()
class Author(Base):
__tablename__ = 'authors'
id = Column(Integer, primary_key=True)
name = Column(String)
pseudo = Column(String)
books = relationship("Book", lazy='joined')
def __repr__(self):
return "<User(name='%s', fullname='%s', password='%s')>" % (
self.name, self.fullname, self.password)
class Book(Base):
__tablename__ = 'books'
id = Column(Integer, primary_key=True)
author_id = Column(Integer, ForeignKey('authors.id'))
name = Column(String)
user = relationship("Author", back_populates="books")
pages = relationship("Page")
class Page(Base):
__tablename__ = 'pages'
id = Column(Integer, primary_key=True)
book_id = Column(Integer, ForeignKey('books.id'))
text = Column(String)
book = relationship("Book", back_populates="pages")
Base.metadata.create_all(engine)
session = Session()
print(str(session.query(Author).outerjoin(Author.books, Page)))
It is by design – read The Zen of Joined Eager Loading:
It is critical to understand the distinction that while Query.join() is used to alter the results of a query, joinedload() goes through great lengths to not alter the results of the query, and instead hide the effects of the rendered join to only allow for related objects to be present.
There are multiple somewhat similar questions in sqlalchemy, though couldn't find one that'd fit the bill exactly.
If you manually add a join, and want to use it to eager load a relationship as well, you need contains_eager():
session.query(Author).\
outerjoin(Author.books, Book.pages).\
options(contains_eager(Author.books).contains_eager(Book.pages))
Note that the relationship definitions Author.books and Book.pages would seem to be missing the back_populates= argument.

How do I add functions to auto-mapped SqlAlchemy classes?

I was wondering if it is possible to use SqlAlchemy's automap_base() in the following way:
from sqlalchemy.ext.automap import automap_base
from sqlalchemy import create_engine
# automap base
Base = automap_base()
# pre-declare User for the 'user' table
class User(Base):
__tablename__ = 'user'
def __str__(self):
return self.name
def greet(self):
return 'Hello {}!'.format(self.name)
def add_address(self, address):
'''Utilizes the auto reflected relationship to add
a new address with a proper user_id to table "addresses"'''
self.address_collection.append(address)
# reflect
engine = create_engine("sqlite:///mydatabase.db")
# only to generate attributes and relationships
Base.prepare(engine, reflect=True)
So I would like to reflect the data and the relationships from the database, but extend the auto-generated class with additional functionality. Is it possible?

How to query with raw SQL using Session or engine

With Parent and Child tables:
from sqlalchemy import Column, ForeignKey, String, create_engine, desc, asc
from sqlalchemy.ext.declarative import declarative_base
import uuid
Base = declarative_base()
class Parent(Base):
__tablename__ = 'parents'
uuid = Column(String(64), primary_key=True, unique=True)
def __init__(self):
self.uuid = uuid.uuid4()
class Child(Base):
__tablename__ = 'children'
uuid = Column(String(64), primary_key=True, unique=True)
parent_uuid = Column(String(64), ForeignKey('parents.uuid'))
def __init__(self, parent_uuid=None):
self.uuid = uuid.uuid4()
self.parent_uuid = parent_uuid
I can go ahead and create a Parent entity:
engine = create_engine('mysql://root:pass#localhost/dbname', echo=False)
session = scoped_session(sessionmaker())
session.remove()
session.configure(bind=engine, autoflush=False, expire_on_commit=False)
parent = Parent()
session.add(parent)
session.commit()
session.close()
The resulting parent variable is a regular Python ORM object.
If I would query a database instead of creating one the result of query would be a list of ORM objects:
result = session.query(Parent).order_by(desc(Parent.uuid)).all()
But there are times when we need to query database using a raw Sql command.
Is there a way to run a raw SQL command using session object so to ensure that the resulting query return is a ORM object or a list of objects?
You can use the execute() method of Session:
session.execute('select * from table')
The execute method's documentation can be found here:
http://docs.sqlalchemy.org/en/latest/orm/session_api.html#sqlalchemy.orm.session.Session.execute
Please note this does not protect against SQL Injection.
With SQLAlchemey 1.4/2.0, you need to wrap the SQL string in an Executable.
from sqlalchemy import text
session.execute(text("select * from table"))

Create a table from a SQLAlchemy model with a different name?

I am importing data from csv files into a table created using the SQLAlchemy declarative api.
I receive updates to this data which I want to stage in a temporary table with the same structure for preprocessing.
E.g:
from sqlalchemy import Column,Integer
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class MyModel(Base):
__tablename__ = "mymodel"
test_column = Column(Integer,primary_key=True)
I can use MyModel.__table__.create() to create this table.
Can I use a similar construct to create another table with the same model and a different name?
What would be the recommended way to achieve this?
Just extend your existing table and change its name
class StagingMyModel(MyModel):
__tablename__ = "staging_mymodel"
This worked for me:
from sqlalchemy import Column,Integer
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class MyBase(Base):
__abstract__ = True
test_column = Column(Integer,primary_key=True)
class MyModel(MyBase):
__tablename__ = "mymodel"
class MyStagingModel(MyBase):
__tablename__ = "mymodel_staging"
Reference: https://docs.sqlalchemy.org/en/14/orm/declarative_tables.html#using-deferredreflection
See also https://sparrigan.github.io/sql/sqla/2016/01/03/dynamic-tables.html for an approach that probably jibes with what you were originally thinking of with ModelName.__table__.create().

creating tables from different models with sqlalchemy

I have different models e.g. model1.py, model2.py etc. Some how tables are being created following pocoo link, which required to be invoked from terminal.
But
def init_db():
import model.model1
import model.model2
Base.metadata.create_all(bind=engine)
This is not working, rather requires to be invoked from terminal.
>> from database import init_db
>> init_db() #works
database.py
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine('sqlite:///xyz.sqlite', echo=True)
db_session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=engine))
Base = declarative_base()
Base.query = db_session.query_property()
def init_db():
import model.admin # from model.admin import User doesnt help either
import model.role
Base.metadata.create_all(bind=engine)
if __name__ == '__main__':
init_db()
admin.py
from sqlalchemy import Column, Integer, String
from database import Base
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String(50), unique=True)
email = Column(String(120), unique=True)
def __init__(self, name=None, email=None):
self.name = name
self.email = email
def __repr__(self):
return '<User %r>' % (self.name)
There are no errors although an empty db file is generated.
How can database be created from multiple models?
I'm not sure why invoking directly from command line triggers table creation for you but I've always structured my Flask apps ala Digital Ocean's guide. Something that wasn't noted explicitly in the quide is the fact that you need to initialize your blueprints first before create_all is able to build the database tables for you.
(Your code as it is, lacks blueprints. Maybe try to create some first then try again?)

Categories

Resources