how to use .query attribute in sqlacodegen models - python

When I use sqlacodegen to create models.py I can't use User.query, getting the warning "[AttributeError: type object 'User' has no attribute 'query']".
I think db.query is a very usable attribute, it's used in SQLAlchemy():
db = SQLAlchemy()
But I need to use sqlacodegen to create models for existing table in our system. Creation code below:
models.py
# coding: utf-8
from sqlalchemy import Column, Date, Float, Integer, String, Table, text
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class User(Base):
__tablename__ = u'users'
id = Column(Integer, primary_key=True)
email = Column(String(64))
username = Column(String(64))
role_id = Column(Integer)
password_hash = Column(String(128))
Now I import models.py:
from models import *
#main.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.name.data).first()
...
When I run this, the warning given is:
AttributeError: type object 'User' has no attribute 'query'
I want to know how I can trans [class User(Base)] to a type like [class User(db.Model)] so it can use .query attribute? Or is there some other usable method to do that with [class User(Base) ]type?

It sounds like you're used to using Flask-SQLAlchemy, which includes the secret sauce to allow the functionality you mention.
The Flask Docs on SQLAlchemy indicate how to get that same functionality:
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine('sqlite:////tmp/test.db', convert_unicode=True)
db_session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=engine))
Base = declarative_base()
Base.query = db_session.query_property()
It'll enable the shorthand of User.query.... that you expect. The important line is the last one. You could alternatively use the db.Model class from Flask-SQLAlchemy instead of the 'pure' SQLAlchemy Base class.

Related

How do I connect existing mysql tables with fastapi?

I want to create an api for login authentication. I have used the below method to create the table, but I don't want to create a new table but instead use an existing table.
main.py
from fastapi import FastAPI
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String
from sqlalchemy.orm import relationship
from database import Base, engine, SessionLocal
class User( Base ):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
is_active = Column(Boolean, default=True)
items = relationship("Item", back_populates="owner")
Base.metadata.create_all(bind=engine)
database.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
SQLALCHEMY_DB_URL = "mysql://root:mumbaicity#localhost:3306/ryr"
engine = create_engine(SQLALCHEMY_DB_URL)
SessionLocal = sessionmaker(autocommit=False, bind=engine)
Base = declarative_base()
conn = engine.connect()
this is the table I want to use: (employee):
enter image description here
You can simply not run Base.metadata.create_all(bind=engine) and define a class for the table you want.
If you have many tables and want to automatically generate model classes, look into sqlacodegen.

FastAPI database SQL

I has a FastAPI project where I have main.py, endpoints.py (with endpoint functions) and users_db.py where I have list of dictionaries. Every dictionary save users data. It just pet project to understand web application. An now I want to create database. I chose SQL. Can somebody explaine or give a link where is described how to do that?! Or is it very hard?!
it's very simple.
first you need install sqlalchemy
pip install SQLAlchemy
then create database.py file and paste the following code to that.
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db"
engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
then create models.py file and write some database models you need.
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String
from sqlalchemy.orm import relationship
from .database import Base
class User(Base):
__tablename__ = "users" # this is table name
id = Column(Integer, primary_key=True, index=True)
email = Column(String, unique=True, index=True)
hashed_password = Column(String)
is_active = Column(Boolean, default=True)
Read the documentation for more information.
documentation

Why sqlalchemy declarative base object has no attribute 'query'?

I created declarative table.
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, String
from sqlalchemy.dialects.postgresql import UUID
import uuid
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, unique=True)
name = Column(String)
I need to filter data. In the Flask-SQLAlchemy, I do
name = 'foo'
User.query.filter_by(name=name).first()
But if I use SQLAlchemy without Flask, then I get the error:
type object 'User' has no attribute 'query'
The only way that works for me is to filter the data through the session.
engine = create_engine('DATABASE_URL')
Session = sessionmaker(bind=engine)
session = Session()
name = 'foo'
user = session.query(User).filter_by(name=name).first()
session.close()
The Model.query... idiom is not a default part of the SQLAlchemy ORM; it's a customisation provided by Flask-SQLAlchemy. It is not available in base SQLAlchemy, and that is why you get the error message.
Why? ...because query object is not set on Base/model object automatically.
Solution is to add this line to your base:
Base.query = SessionLocal.query_property()
(where SessionLocal is instance of scoped_session)
This will make query available on all of your models and should solve your issue.However, mypy and pylint will still be complaining about this (that's how I found this question).

SQLAlchemy 'no such table' when trying to reference session from imported module

I have an app I am using where all of my models and my session are stored within a single models.py file:
import datetime
from sqlalchemy import create_engine, ForeignKey
from sqlalchemy import Column, Date, Integer, String, DateTime, Float
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.hybrid import hybrid_property, hybrid_method
engine = create_engine('sqlite:///bidbot.db', echo=True)
Base = declarative_base()
Session = sessionmaker(bind=engine)
session = Session()
class AgentLog(Base):
__tablename__ = 'agentlog'
id = Column(Integer, primary_key=True)
created_at = Column(DateTime,default=datetime.datetime.utcnow)
ingroups = Column(String)
total_calls = Column(Integer)
calls_waiting = Column(Integer)
agents_logged_in = Column(Integer)
agents_in_calls = Column(Integer)
agents_waiting = Column(Integer)
agents_paused = Column(Integer)
agents_in_dispo = Column(Integer)
agents_in_dial = Column(Integer)
def create_new():
session.add(AgentLog())
session.commit()
As you can see, I have a custom create_new method which uses the session to create a new object.
If I run this models.py file on its own:
python -i models.py
Then I can call the AgentLog.create_new() function and it creates a new record.
However, I am importing models.py into my higher level package:
from models.py import *
And when I run the same function on the model:
AgentLog.create_new()
I now get a no such table exists error... Which is confusing because I believe the method should have access to the exact same session object.
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: agentlog
Figured it out! When I imported the package, it was defining the engine with the relative path based on the importing directory, not the models.py module.
I needed to update the path to use relative directory based on the models.py file:
import os
DATABASE_DIR = os.path.dirname(os.path.abspath(__file__)) + '\\bidbot.db'
engine = create_engine('sqlite:///'+DATABASE_DIR, echo=True)

lazy=True in (Flask-)SQLAlchemy

I'm learning SQLAlchemy and I want to make sure that I've understood the backref parameter in relationship correctly.
For example
from app import db
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True)
posts = db.relationship('Post', backref='author', lazy=True)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.String(140))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
Say I have a User object j = models.User.query.get(1). My question is, is there any difference between the following things?
j.posts
Post.query.filter_by(author=j).all()
Post.query.with_parent(j).all()
Post.query.with_parent(j, property='posts').all()
Post.query.with_parent(j, property=User.posts).all()
The results returned are same, but I don't know whether the SQL statements executed are identical.
What I've tried
The SQLAlchemy docs says:
with_parent(instance, property=None, from_entity=None)
...the given property can be None, in which case a search is performed against this Query object’s target mapper.
So the last three statements seem same, but I don't really understand what does this Query object’s target mapper refer to. Is it Post in this case, for this query is performed on Post?
Even if the generated SQL statements are identical, the commands you enlisted may have a different impact on your application, e.g. j.posts will cache (memoize, do not confuse with Werkzeug caching) results you have got, while others will fetch them every single time.
If you remove .all() from your queries you can simply print them:
query = Post.query.filter_by(author=j)
print(query)
Which would result in:
SELECT post.id AS post_id, post.body AS post_body, post.user_id AS post_user_id
FROM post
WHERE ? = post.user_id
Using .all() is essentially like getting [m for m in query]).
The trick with query-printing will not work for j.posts which will return something like:
> print(j.posts)
> [Post(...), Post(..)]
Still, you can see all the silently emitted queries using built-in sqlalchemy loggers. See the following code:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.engine import Engine
from sqlalchemy import event
import logging
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/tests.db'
db = SQLAlchemy(app)
logging.basicConfig()
logger = logging.getLogger('sqlalchemy.engine')
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True)
posts = db.relationship('Post', backref='author', lazy=True)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.String(140))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
db.drop_all()
db.create_all()
user = User(username='test', posts=[Post(body='some body')])
db.session.add(user)
db.session.commit()
# start logging
logger.setLevel(logging.DEBUG)
j = User.query.get(1)
queries = {
"j.posts",
"Post.query.filter_by(author=j)",
"Post.query.with_parent(j)",
"Post.query.with_parent(j, property='posts')",
"Post.query.with_parent(j, property=User.posts)",
}
def test_queries():
for name in queries:
print('\n=======')
print('Executing %s:' % name)
query = eval(name)
print(query)
test_queries() # you should see j.posts query here
print('Second test')
test_queries() # but not here
Getting back to your question: yes, the emitted SQL queries are identical.
In Query object’s target mapper, Query object's target refers to Post in your example. Decoupling this, when you declare Post class, inheriting from db.Model, for SQLAlchemy it is like creating an object Post and mapping the properties of this object to columns of specially created table.
Underneath there is an instance of Mapper class, which is responsible for the mapping for every single model that you create (learn more about mapping here: Types of Mappings). You can simply get this mapper calling class_mapper on your model or object_mapper on an instance of your model:
from sqlalchemy.orm import object_mapper, class_mapper,
from sqlalchemy.orm.mapper import Mapper
assert object_mapper(j) is class_mapper(User)
assert type(class_mapper(User)) is Mapper
The Mapper has all the necessary information about the columns and relations you have in your model. When calling Post.query.with_parent(j) this information is used to find a property (i.e. relationship) relating Post and User objects, so in your case to populate 'property' with User.posts.
To see the queries you can run your python script with -i and then run each query individually and it will print out the SQL code it runs.
Example:
main.py:
import sqlalchemy
from sqlalchemy import create_engine, Column, Integer, String, Sequence
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
import os
engine = create_engine('sqlite:///:memory:', echo=True)
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, Sequence('user_id_seq'), primary_key=True)
name = Column(String(50))
fullname = Column(String(50))
password = Column(String(12))
def __repr__(self):
return "< User(name={}, fullname={}, password={} )>".format(self.name, self.fullname, self.password)
Base.metadata.create_all(engine)
ed_user= User(name='ed', fullname='Ed Jones', password='edpassword')
Session = sessionmaker(bind=engine, autoflush=False)
session = Session()
session.add(ed_user)
session.add_all([
User(name='wendy', fullname='Wendy Williams', password='foobar'),
User(name='mary', fullname='Mary Contraty', password='xxg527'),
User(name='fred', fullname='Fred Flinstone', password='blah')
])
session.commit()
os.system('clear')
Now you run it with python -i main.py, type: session.query(User).filter_by(name='ed').first() and you will see the SQL generated. After running all of your tests I concluded that they are all identical. With this method you can test any query and see if there is any difference.
p.s. I added the os.system('clear') to remove all the unnecessary output from creating the database and some other stuff.

Categories