Generate schema SQL with SQLAlchemy - python

How does one generate the SQL/Migrate Code/Whatever with SQLAlchemy when using the Declarative Base?
from sqlalchemy import *
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
engine = create_engine('mysql://root:password#localhost/mydb_dev', echo=True)
metadata = MetaData(bind=engine)
class User(Base):
__table__ = Table("users", metadata, autoload=True)
id = Column(Integer, primary_key=True)
display_name = Column(String)
email = Column(String)
password = Column(String)
def __repr__(self):
return "<User(id='{}', display_name='{}', email='{}')>".format(self.id, self.display_name, self.email)
class Site(Base):
__table__ = Table("sites", metadata, autoload=True)
id = Column(Integer, primary_key=True)
name = Column(String)
urls = relationship("URL")
def __repr__(self):
return "<Site(id='{}', name='{}')>".format(self.id, self.name)
I have this so far, I'd like to see what SQLAlchemy would generate as a schema.
Or, does SQLAlchemy do this at all? Is this a case where I create and manage the database and it's schema separately, and I just update my entities to reflect it?
Do understand that I am used to Doctrine2, and im very new to SQLAlchemy
Thanks!

You can create the db models by calling:
metadata.create_all()
http://docs.sqlalchemy.org/en/latest/core/metadata.html#sqlalchemy.schema.MetaData.create_all
Note that this only works for the creation of previously non-existent models, and doesn't handle updates or downgrades. Check out alembic for even finer control:
http://alembic.zzzcomputing.com/en/latest/

Related

Alembic Build Python ORM files from database

I have a database with nearly 100 tables. I am trying to use alembic to manage it in python. Is there a way for alembic to build the ORM python files for me or do I have to write them myself.
For example in the database there is a user table with columns id, first_name, last_name, email is there a way alembic can pull this from the database and basically write the following file for me:
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey
​
Base = declarative_base()
​
class User(Base):
__tablename__ = 'user'
​
id = Column(Integer, primary_key=True)
first_name = Column(String)
last_name = Column(String)
email = Column(String)
Thanks

SQLalchemy: Could not determine join condition between parent/child tables on relationship

So I have these two simple classes inside the same python file which I'm trying to map with SQLAlchemy, where User column 'password_id' is a foreign key to the table Password 'password_id' column as well
from sqlalchemy.orm import relationship, declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy.sql.schema import ForeignKey
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
user_id = Column(Integer, primary_key=True)
name = Column(String)
email = Column(String)
password_id = Column(Integer, ForeignKey('password.password_id'))
parent = relationship("Password", back_populates="users")
class Password(Base):
__tablename__ = 'passwords'
password_id = Column(Integer, primary_key=True)
password = Column(String)
last_change_date = Column(DateTime)
valid_until = Column(DateTime)
child = relationship("User", back_populates="passwords", uselist=False)
Here's the db schema for context:
I'm following this guide from sql alchemy but for some reason I keep getting the error from the title 'Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression.', which is strange because accorind to what I see in the guide, my classes have been mapped correctly so I can't understand why this error is happening.
Thank you in advance
I think the issue is in the following line (the table name in ForeignKey)...
password_id = Column(Integer, ForeignKey('password.password_id'))
should be passwords instead of password.

SQLAlchemy classes across files, tables are not created

I am trying to create an application using SQLAlchemy. It worked fine as long as I only had one file with one Class. Now I want to have multiple classes/tables in different files. I stumbled upon this question, and tried to do it like it was suggested there: I now have three files
base.py
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
blind.py
from sqlalchemy import Column, String
from .base import Base
class Blind(Base):
__tablename__ = 'blinds'
blind = Column(String)
data_processor_uuid = Column(String, primary_key=True)
data_source_uuid = Column(String)
timestamp = Column(String, primary_key=True)
and data.py
from sqlalchemy import Column, Integer, String, Float
from .base import Base
class Datum(Base):
__tablename__ = 'data'
data_source_uuid = Column(Integer, primary_key=True)
sensor_type = Column(String)
timestamp = Column(String, primary_key=True)
value = Column(Float)
I now want to initialize the database in db_setup.py using
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from .base import Base
engine = create_engine('sqlite:///test.db', echo=True)
Base.metadata.bind = engine
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
def get_db_session():
return session
This works, however, it does not create the tables in the database as expected. When I try to insert something into the table, I get an error saying "table does not exist". Can someone tell me what I am doing wrong here?
The problem was that I wasn't importing the class definitions for Blinds and Datum anywhere, so they weren't evaluated! Before I split them up into different files, I had imported them to get to Base. Thanks to #IljaEverilä for this answer!

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.

sqlalchemy + flask: class is not defined

i'm using sqlalchemy + alembic + Flask and i can't map circular classes.
apps/users/models.py:
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
email = Column(String)
password = Column(String)
session = relationship("Session", back_populates='user', cascade='all,delete', lazy='dynamic')
notes = relationship('Note2User', back_populates='user', cascade='all,delete', lazy='dynamic')
apps/notes/models.py:
class Note2User(Base):
__tablename__ = 'notes_users_m2m'
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('users.id', ondelete='CASCADE'), nullable=False)
user = relationship('User', back_populates='notes')
note_id = Column(Integer, ForeignKey('notes.id', ondelete='CASCADE'), nullable=False)
note = relationship('Note', back_populates='users')
Table Note2User made for m2m relationship User <-> Notes, but when i start app and done some request, gets error:
InvalidRequestError: When initializing mapper Mapper|User|users,
expression 'Note2User' failed to locate a name ("name 'Note2User' is
not defined"). If this is a class name, consider adding this
relationship() to the class after
both dependent classes have been defined.
Initializing db in db/init.py: (dunder name)
from sqlalchemy import create_engine, MetaData
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session
engine = create_engine('postgresql+psycopg2://server:12345#localhost:5432/test')
Base = declarative_base()
meta = MetaData()
meta.reflect(bind=engine)
db_session = Session(bind=engine)
Add an import for Note2User class in apps/users/models.py file so this model gets defined first before initializing that relatioship in User class which refrences it.
like this
# file: apps/users/models.py
from ..notes.models import Note2User
You need to import the user.models module into the notes.model module and vice versa. It would look something like this:
# file app/users/models.py
import app.notes.models as notes
# use it like this
notes.Notes2User()
# file app/notes/models.py
import app.users.models as users
users.User()
The advantage to this is that you will avoid circular dependency problems as your program inevitably grows. I had so many problems with circular dependencies when I was creating an app with your same stack. The only solution was to ditch the
from . import Foo
and only use
import bar.foo as foo
It is considered best practice to use the import syntax for this reason.
Reference.

Categories

Resources