AttributeError: 'set' object has no attribute '_sa_instance_state' - SQLAlchemy - python-3.x

Im trying to input multiple requests to one session, the idea is to log http traffic with mitmproxy but im still constructing the database, im sure there is something bad in my models but i cannot find what
The idea is to Store multiple requests in one session, accesing easily to each requests response if needed.
db.py
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
schema_name = 'Traffic'
engine = create_engine('postgresql://xxx:xxx#localhost:5432/xxx')
if not engine.dialect.has_schema(engine, schema_name):
engine.execute(sqlalchemy.schema.CreateSchema(schema_name))
Session = sessionmaker(bind=engine)
session = Session()
Base = declarative_base()
Request.py
from sqlalchemy.orm import relationship
from entities.utils import db
from entities.utils.utils import _repr
from sqlalchemy import Column, String, JSON, INTEGER, ForeignKey, Integer, DateTime
class RequestModel(db.Base):
__tablename__ = 'requests'
id = Column(Integer, primary_key=True,autoincrement=True)
headers = Column(JSON,nullable=False)
method = Column(String,nullable=False)
size = Column(INTEGER,nullable=False)
timestamp_end = Column(DateTime,nullable=False)
timestamp_start = Column(DateTime,nullable=False)
status_code = Column(INTEGER,nullable=False)
body = Column(JSON,nullable=True)
url = Column(String,nullable=False)
response_id = Column(INTEGER,ForeignKey('responses.id'))
def __init__(self,method,status_code,url):
self.method = method
self.status_code = status_code
self.url = url
Session.py
from sqlalchemy.orm import relationship
from entities.utils import db
from entities.utils import utils
from sqlalchemy import Column, String, Integer, ForeignKey
class SessionModel(db.Base):
__tablename__ = 'sessions'
id = Column(Integer, primary_key=True,autoincrement=True)
server = Column(String, nullable=False)
world = Column(String,nullable=False)
user = Column(String,nullable=False)
version = Column(String,nullable=False)
requests_id = Column(Integer, ForeignKey('requests.id'))
requests = relationship('RequestModel')
def __init__(self,server,world,user,version):
self.server = server
self.world = world
self.user = user
self.version = version
def __repr__(self):
return self._repr(id=self.id,server=self.server,
world=self.world,user=self.user,version=self.version)
Response.py
from sqlalchemy.orm import relationship, declarative_base
from entities.utils import db
from entities.utils.utils import _repr
from sqlalchemy import Column, JSON, INTEGER, ForeignKey, Integer
class ResponseModel(db.Base):
__tablename__ = 'responses'
id = Column(Integer, primary_key=True, autoincrement=True)
headers = Column(JSON, nullable=False)
status_code = Column(INTEGER, nullable=False)
body = Column(JSON, nullable=True)
def __init__(self, headers, status_code, body):
self.headers = headers
self.status_code = status_code
self.body = body
def __repr__(self):
return self._repr(id=self.id, status=self.status_code)
Input.py
import json
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
from sqlalchemy.ext.declarative import declarative_base
import datetime
from entities.models.Request import RequestModel
from entities.models.Response import ResponseModel
from entities.models.Session import SessionModel
engine = create_engine('postgresql://XXX:XXX#localhost:5423/XXXX')
Base = declarative_base()
# create session and add objects
with Session(engine) as session:
response_object = ResponseModel(body={'22':'22'},status_code=402,headers={'test':'test'})
session.add(response_object)
session.commit()
object_1 = RequestModel('POST', 402, 'www.intersecting.com/url?=parameter1&key')
object_1.headers = {'test':'19'}
object_1.body = {'test':'19'}
object_1.size = 512
object_1.timestamp_start = datetime.datetime.now()
object_1.timestamp_end =datetime.datetime.now()
object_2 = object_1
session.add(object_1)
session.commit()
session_object = SessionModel('www.test.com','test62','admin','3.12.13')
print(session_object)
session_object.requests = {object_2,object_1}
session.add(session_object)
session.commit()
But im not able to store 2 requests in one session i get this error.
Traceback (most recent call last):
File "C:/Users/qwert/PycharmProjects/Tribalwars/entities/utils/examples/input.py", line 35, in <module>
session.add(session_object)
File "C:\Users\qwert\AppData\Local\Programs\Python\Python38\lib\site-packages\sqlalchemy\orm\session.py", line 2573, in add
self._save_or_update_state(state)
File "C:\Users\qwert\AppData\Local\Programs\Python\Python38\lib\site-packages\sqlalchemy\orm\session.py", line 2589, in _save_or_update_state
for o, m, st_, dct_ in mapper.cascade_iterator(
File "C:\Users\qwert\AppData\Local\Programs\Python\Python38\lib\site-packages\sqlalchemy\orm\mapper.py", line 3166, in cascade_iterator
queue = deque(
File "C:\Users\qwert\AppData\Local\Programs\Python\Python38\lib\site-packages\sqlalchemy\orm\relationships.py", line 1982, in cascade_iterator
tuples = state.manager[self.key].impl.get_all_pending(state, dict_)
File "C:\Users\qwert\AppData\Local\Programs\Python\Python38\lib\site-packages\sqlalchemy\orm\attributes.py", line 1169, in get_all_pending
ret = [(instance_state(current), current)]
AttributeError: 'set' object has no attribute '_sa_instance_state'

Related

SQLAlchemy + FasyAPI = EmpyDatabase Table Without Tables

Im trying to create a new DataBase with SQLAlchemy using FastAPI but when i run the server i get a empty DB file without tables.
database.py
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = "sqlite:///./fastapi-test.db"
engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
models.py
from db.database import Base
from sqlalchemy import Column, Integer, String
class DbUser(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True, index=True)
username = Column(String)
email = Column(String)
password = Column(String)
main.py
from fastapi import FastAPI
from routers import blogs_get, blog_post
from db import models
from db.database import engine
app = FastAPI()
app.include_router(blogs_get.router)
app.include_router(blog_post.router)
#app.get('/')
def index():
return 'Hello World'
models.Base.metadata.create_all(engine)
I already try to rewrite all the model. but i think i have an issue importing the Base component from Database

SQLAlchemy : NoForeignKeysError: Could not determine join condition

I'm writing a site for a Flask,and I made a mistake when I want to connect migrations, shows this error
sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between parent/child tables on relationship Users.roles - there are no foreign keys
linking these tables via secondary table 'сайт.role_users'. Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or
specify 'primaryjoin' and 'secondaryjoin' expressions.
Here is my code:
from flask import Flask, render_template, request, flash, \
redirect, url_for
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
from flask_migrate import Migrate
from flask_security import RoleMixin
import os
css_file = 'static/css/main.css'
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///lib.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SECRET_KEY'] = os.getenv('SECRET_KEY')
db = SQLAlchemy(app)
manager = LoginManager(app)
migrate = Migrate(app, db)
role_users = db.Table('role_users',
db.Column('user_id', db.Integer(), db.ForeignKey('user.id')),
db.Column('role_id', db.Integer(), db.ForeignKey('role.id')),
)
class Users(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
login = db.Column(db.String(75), nullable=False, unique=True)
password = db.Column(db.String(80), nullable=False)
roles = db.relationship('Roles', secondary=role_users, backref=db.backref('users',
lazy='dynamic'))
def __repr__(self):
return f'<User> -----> {self.login}'
class Roles(db.Model, RoleMixin):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), unique=True, nullable=False)
def __repr__(self):
return f'<Role> ----> {self.title}'
Although there were no such errors during the first migration
.......
Help solve this problem

InValid Pydantic Field Type POST parameters (FastApi) - Python 3.8

I am attempting to use FastApi + SqlAlchemy And am having an issue with Parameters. This is the Error my Post parameter is causing. What am i doing wrong?
ERROR MESSAGE
fastapi.exceptions.FastAPIError: Invalid args for response field! Hint: check that <class 'app.models.Post'> is a valid pydantic field type
Code
from fastapi import FastAPI, Response, HTTPException
from fastapi.params import Body, Depends
from sqlalchemy.orm import Session
from starlette import status
from . import models
from .database import engine, get_db
from .models import Post
models.Base.metadata.create_all(bind=engine)
app = FastAPI()
#app.get("/posts")
def get_posts(db: Session = Depends(get_db)):
posts = db.query(models.Post).all()
return {"data": posts}
#app.post("/posts", status_code = status.HTTP_201_CREATED)
def create_posts(post: Post, db: Session = Depends(get_db)):
new_post = models.Post(title=post.title, content=post.content, published=post.published)
# db.add(new_post)
# db.commit()
# db.refresh(new_post)
return {"data": new_post}
Post Type
from sqlalchemy import Column, Integer, String, Boolean, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.sql.expression import text
from sqlalchemy.sql.sqltypes import TIMESTAMP
from .database import Base
class Post(Base):
__tablename__ = "postsalchemy"
id = Column(Integer, primary_key=True, nullable=False)
title = Column(String, nullable=False)
content = Column(String, nullable=False)
published = Column(Boolean, server_default='TRUE', nullable=False)
created_at = Column(TIMESTAMP(timezone=True),
nullable=False, server_default=text('now()'))
FastAPI uses Pydantic library to validate both HTTP request and HTTP response body. ( BTW Pydantic is a library that will provide you validation of data structure against a given schema )
For that purpose, you must create a Pydantic Model class that corresponds to your sqlalchemy Post class.
Please read the docs here to learn more about response model.
FYI, here is how to do with your example :
from fastapi import FastAPI, Response, HTTPException
from fastapi.params import Body, Depends
from sqlalchemy.orm import Session
from starlette import status
from . import models
from .database import engine, get_db
from .models import Post
from pydantic import BaseModel
from datetime import datetime
from typing import Dict, List
# Define your pydantic response model
class PostResponse(BaseModel):
id: int
title: str
content: str
published: bool
created_at: datetime
models.Base.metadata.create_all(bind=engine)
app = FastAPI()
# Set PostResponse as a response model in the route decorator
#app.get("/posts", response_model=Dict[str, List[PostResponse]])
def get_posts(db: Session = Depends(get_db)):
posts = db.query(models.Post).all()
return {"data": posts}
#app.post("/posts", status_code = status.HTTP_201_CREATED, response_model=Dict[str, PostResponse])
def create_posts(post: Post, db: Session = Depends(get_db)):
new_post = models.Post(title=post.title, content=post.content, published=post.published)
# db.add(new_post)
# db.commit()
# db.refresh(new_post)
return {"data": new_post}
EXTRA : You might be also interested in SQLModel library which is from the same author of FastAPI and is a combination between SQL Alchemy and Pydantic library and basically allow you to avoid duplication (and declare your model only once)

Why is "Object of type Decimal is not JSON serializable" - when using marshmallow with SQLAlchemy automap?

Using automap_base from sqlalchemy.ext.automap to map my tables.
Not able to shema.dumps(result);
getting
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type Decimal is not JSON serializable
Tried using JSON custom decoders, but no use.
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm import Session
from sqlalchemy.ext.automap import automap_base
from flask_marshmallow import Marshmallow
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db'
db = SQLAlchemy(app)
ma = Marshmallow(app)
engine = db.engine
session = Session(engine)
Base = automap_base()
Base.prepare(engine, reflect=True)
MyTable = Base.classes.my_table
class MyTableSchema(ma.ModelSchema):
class Meta:
model = MyTable
#app.route("/")
def api():
all_rows = session.query(MyTable).all()
schema = MyTableSchema(many=True)
response = schema.dumps(all_rows)
return response
if __name__ == '__main__':
app.run(debug=True)
An easy workaround to transform an SQLAlchemy result object in JSON is using simplejson.
You just need to import it (import simplejson) and it works.
Using your example:
import simplejson
...
#app.route("/")
def api():
all_rows = session.query(MyTable).all()
response = simplejson.dumps(all_rows)
Add .data to response like below
def api():
all_rows = session.query(MyTable).all()
schema = MyTableSchema(many=True)
response = schema.dumps(all_rows)
return response.data

sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table

I defined table name users_table and run db.create_all() to create the table, but get error "no such table user_table" on commit for updating user info.
How I test :
(under /project) python3 manage.py shell
>>> u = User(email='foo#bar.com', username='foobar', password='player')
>>> db.create_all()
>>> db.session.add(u)
>>> db.session.commit() # with following error message
Traceback (most recent call last):
File "C:\...\Python\Python36-32\lib\site-packages\sqlalchemy\engine\base.py", line 1182, in _execute_context
context)
File "C:\...\Python\Python36-32\lib\site-packages\sqlalchemy\engine\default.py", line 470, in do_execute
cursor.execute(statement, parameters)
sqlite3.OperationalError: no such table: users_table
...
...
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: users_table
/project/app/_init_.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config import config
db = SQLAlchemy()
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app)
db.init_app(app)
return app
/project/app/models.py:
import os
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash
from flask import Flask
basedir = os.path.abspath(os.path.dirname(__file__))
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'data.sqlite')
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
db = SQLAlchemy(app)
class User(db.Model):
__tablename__ = 'users_table'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(64), unique=True, index=True)
username = db.Column(db.String(64), unique=True, index=True)
password_hash = db.Column(db.String(128))
def __repr__(self):
return '<User %r>' % self.username
#property
def password(self):
raise AttributeError('Password is not a readable attribute')
#password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
project/config.py:
import os
basedir = os.path.abspath(os.path.dirname(\__file__))
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'fhuaioe7832of67^&*T#oy93'
SQLALCHEMY_COMMIT_ON_TEARDOWN = True
#staticmethod
def init_app(app):
pass
class DevelopmentConfig(Config):
DEBUG = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'data.sqlite')
config = {
'development': DevelopmentConfig,
'default': DevelopmentConfig,
}
project/manage.py:
import os
from app import create_app, db
from app.models import User
from flask_script import Manager, Shell
app = create_app(os.getenv('FLASK_CONFIG') or 'default')
manager = Manager(app)
def make_shell_context():
return dict(app=app, db=db, User=User)
manager.add_command("shell", Shell(make_context=make_shell_context))
if __name__ == '__main__':
manager.run()
I just got done setting up a Flask app and I dealt with this kind of problem.
I strongly suspect the problem here is that the instance of db that you are creating in __init__.py is unaware of the contents of models.py, including the User class. The db object in __init__.py is a totally separate object from the db you are creating in models.py. So when you run db.create_all() in __init__.py, it is checking the list of tables that it knows about and isn't finding any. I ran into this exact issue.
What I discovered is that the models (like User) are registered with the particular db object that is listed in the model's class definition (e.g. class User(db.Model):).
So basically my understanding is that the way to fix this is to run db.create_all() using the same instance of db that is being used to define the models. In other words, run db.create_all() from within models.py.
Here's my code so you can see how I have it set up:
app.py:
#!flask/bin/python
import os
from flask import Flask
class CustomFlask(Flask):
jinja_options = Flask.jinja_options.copy()
jinja_options.update(dict(
variable_start_string='%%', # Default is '{{', I'm changing this because Vue.js uses '{{' / '}}'
variable_end_string='%%',
))
app = CustomFlask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'
import yaml
if os.environ['SERVER_ENVIRONMENT'] == 'PRODUCTION':
config_filename = "production.yaml"
elif os.environ['SERVER_ENVIRONMENT'] == 'LOCAL':
config_filename = "local.yaml"
else:
config_filename = "local.yaml"
base_directory = path = os.path.dirname(os.path.realpath(__file__))
with open(base_directory + "/config/" + config_filename) as config_file:
config = yaml.load(config_file)
db_config = config['database']
SQLALCHEMY_DATABASE_URI = "mysql+mysqlconnector://{username}:{password}#{hostname}/{databasename}".format(
username=db_config['username'],
password=db_config['password'],
hostname=db_config['hostname'],
databasename=db_config['databasename'],
)
app.config["SQLALCHEMY_DATABASE_URI"] = SQLALCHEMY_DATABASE_URI
app.config["SQLALCHEMY_POOL_RECYCLE"] = 299
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy(app)
db.app = app
def clear_the_template_cache():
app.jinja_env.cache = {}
app.before_request(clear_the_template_cache)
from flask_login import LoginManager
login_manager = LoginManager()
login_manager.init_app(app)
#login_manager.user_loader
def load_user(email):
from models import User
return User.query.filter_by(email=email).first()
if __name__ == '__main__':
from routes import web_routes
app.register_blueprint(web_routes)
from api import api
app.register_blueprint(api)
# To get PyCharm's debugger to work, you need to have "debug=False, threaded=True"
#app.run(debug=False, threaded=True)
app.run(debug=True)
models.py:
from app import db
import datetime
from werkzeug.security import generate_password_hash, \
check_password_hash
class Song(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(80))
datetime_created = db.Column(db.DateTime, default=datetime.datetime.utcnow())
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
lines = db.relationship('Line', cascade="all,delete", backref=db.backref('song', lazy='joined'), lazy='dynamic')
is_deleted = db.Column(db.Boolean, default=False)
class Line(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
song_id = db.Column(db.Integer, db.ForeignKey('song.id'))
spans_of_time = db.relationship('SpanOfTime', cascade="all,delete", backref=db.backref('line', lazy='joined'), lazy='dynamic')
class SpanOfTime(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
line_id = db.Column(db.Integer, db.ForeignKey('line.id'))
starting_64th = db.Column(db.Integer) # I'm assuming the highest-granularity desired will be a 1/64th note-length.
length = db.Column(db.Integer) # I guess this'll be in 1/64th notes, so a 1/16th note will be '4'.
content = db.Column(db.String(80))
class User(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
email = db.Column(db.String(80), primary_key=True, unique=True)
display_name = db.Column(db.String(80), default="A Rhymecraft User")
password_hash = db.Column(db.String(200))
datetime_subscription_valid_until = db.Column(db.DateTime, default=datetime.datetime.utcnow() - datetime.timedelta(days=1))
datetime_joined = db.Column(db.DateTime, default=datetime.datetime.utcnow())
songs = db.relationship('Song', cascade="all,delete", backref=db.backref('user', lazy='joined'), lazy='dynamic')
def __init__(self, email, password):
self.email = email
self.set_password(password)
def __repr__(self):
return '<User %r>' % self.email
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
def is_authenticated(self):
return True
def is_active(self):
return True
def is_anonymous(self):
return False
def get_id(self):
return str(self.email)
def init_db():
db.create_all()
# Create a test user
new_user = User('a#a.com', 'aaaaaaaa')
new_user.display_name = 'Nathan'
db.session.add(new_user)
db.session.commit()
new_user.datetime_subscription_valid_until = datetime.datetime(2019, 1, 1)
db.session.commit()
if __name__ == '__main__':
init_db()
Very simple solution: in the app.py or main.py you can just add these lines of code for fixing this issue:
#app.before_first_request
def create_tables():
db.create_all()
In your case, require to add following code into __init__.py:
from models import User, Role
#app.shell_context_processor
def make_shell_context():
return dict(db=db, User=User, Role=Role)
then you do your previous works, it's all work.
I run into the same problem, after doing a YT tutorial. I solved it by adding this code at the end of my __init__.py
from .models import User, Post
with app.app_context():
db.create_all()
Sidenote: Most tutorials don't use the with app.app_context(): . I think there was a update in flask, which is why this is needed. This caused errors in my code and maybe it helps someone who reads this.
I would like to mention that it was the flask tutorial from "corey schafer" after "part 6 - user authentication", and the error appeared when i ran some tests. just in case anyone else is doing the exact same tutorial and therfore finds it easier to identify my answer as helpful. I am not mentioning the creater for advertisement. I hope this is ok.
Create a folder named "instance" in the root directory and move your database file to that folder.

Resources