crud functions in sqlalchmey orm - python-3.x

so I was trying to implement CRUD functions on my code so i'm trying to create a session first but it keeps giving me an error "engine is not defined" so I tried to define it but I keep getting the same error. the code is creating a database 3 tables (user, account, place)using sqlalchemy ORM. what am I doing wrong?
from sqlalchemy import create_engine, Column, Integer, Boolean, String, ForeignKey, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm.query.Query
Session = sessionmaker(bind=engine)
Base = declarative_base()
class User(Base):
__tablename__ = "user"
id = Column('id',Integer,primary_key=True , autoincrement=True)
username = Column('username',String , unique=True)
admin = Column('admin',Boolean )
password = Column('password' , String)
email = Column('email' , String , unique=True)
p_id = Column(Integer , ForeignKey('place.id'))
googleassist = Column('googleassist' , String)
account = relationship("Account", ## Cascade to delete associated account
back_populates='user', ## if that user was deleted.
cascade='all, delete-orphan')
class Account(Base):
__tablename__ = "account"
id = Column('id', Integer,primary_key=True,autoincrement=True)
user_id = Column(Integer , ForeignKey('user.id'))
class Place(Base):
__tablename__ = "place"
id = Column('id', Integer,primary_key=True,autoincrement=True)
p_location = Column('p_location' , String)
h_id = Column('h_id',Integer)
Session.configure(bind=engine)
engine = create_engine('sqlite:///withServer.db')
Base.metadata.create_all(engine)
session = Session(engine)
#creating objects
u1=User( id = '1' ,
username = 'ali',
admin = '1',
password = 'pass',
email = 'haifa#gmail.com'
)
session.add(u1)
session.commit()
#update
i = session.query(User)
i.password = 'passtoo'
session.add(i)
session.commit()
#delete
i = session.query(Account).filter(Account.id == '1').one()
session.delete(i)
session.commit()

indeed you just use engine without defining it first:
Session = sessionmaker(bind=engine)
you need to have the definition of your engine before that line. move
engine = create_engine('sqlite:///withServer.db')
before the first usage of engine.

Related

FastAPI - Set response_model_exclude when calling API

In FastAPI I have a situation similar to this:
models.py
class MachineGroups(Base):
__tablename__ = 'MachineGroups'
MachineGroupsId = Column(NVARCHAR(25), primary_key=True)
Description = Column(NVARCHAR(255))
class Machines(Base):
__tablename__ = 'Machines'
MachinesId = Column(NVARCHAR(50), primary_key=True)
Description = Column(NVARCHAR(255))
MachineGroupsId = Column(NVARCHAR(25), ForeignKey("MachineGroups.MachineGroupsId", ondelete="SET NULL"), nullable=True)
group = relationship("MachineGroups")
schemas.py
class MachineGroups(BaseModel):
MachineGroupsId: str
Description: str
class Config:
orm_mode = True
class Machines(BaseModel):
MachinesId: str
Description: str
MachineGroupsId: str = None
group: Optional[MachineGroups] = None
class Config:
orm_mode = True
In the controller I have a function create like this:
controller.py
#app.get(
"/machines",
response_model=List[machine_schemas.Machines],
response_model_exclude={'group'}
)
def get_machines(db: Session = Depends(get_db)):
return db.query(machine_models.Machines).all()
I would like to be able to set the response_model_exclude field value in the decorator directly from the API call. In practice I would like to have a query parameter on the function that allows me allows me to get the foreign key information or not.
To avoid having this situation:
#app.get(
"/machines",
response_model=List[machine_schemas.Machines],
response_model_exclude={'group'}
)
def get_machines(db: Session = Depends(get_db)):
return db.query(machine_models.Machines).all()
#app.get(
"/machines/all",
response_model=List[machine_schemas.Machines],
)
def get_machines_all(db: Session = Depends(get_db)):
return db.query(machine_models.Machines).all()
Is it possible to achieve this?
Thank you.
I solved the problem by creating a custom JSONResponse and using the jsonable_encoder function to exclude the foreign key fields.
#app.get(
"/machines/all",
response_model=List[machine_schemas.Machines],
)
def get_machines_all(get_foreign_keys: bool = True, db: Session = Depends(get_db)):
machines = db.query(machine_models.Machines).all()
if get_foreign_keys:
return machines
else:
return JSONResponse(jsonable_encoder(machines, exclude={'group'}))

AttributeError: 'set' object has no attribute '_sa_instance_state' - SQLAlchemy

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'

How to create an interface table in sqlAlchemy (relation many-to-many)

I'm using SqlAlchemy to interact with an existing Mariadb database. I need to access data organized in a many-to-many relationship. According to the source site documentation (sqlAlchemy), I created a multi-to-multiple relationship, but after entering the flask db migrate command, I get an error.
It should be noted that the tables of posts and categories have been created.
Documents for creating the interface table:
from sqlalchemy import Column, String, Text, Integer, Table, ForeignKey
from app import db
posts_categories = Table('posts_categories', db.metadata,
Column('post_id', Integer, ForeignKey('posts.id', ondelete='cascade')),
Column('category_id', Integer, ForeignKey('categories.id', ondelete='cascade'))
)
class Category(db.Model):
__tablename = 'categories'
id = Column(Integer, primary_key=True)
name = Column(String(128), nullable=False, unique=True)
description = Column(String(256), nullable=True, unique=False)
slug = Column(String(128), nullable=False, unique=True)
posts = db.relationship('Post', secondary=posts_categories, back_populates='categories')
class Post(db.Model):
__tablename = 'posts'
id = Column(Integer, primary_key=True)
title = Column(String(128), nullable=False, unique=True)
summary = Column(String(256), nullable=True, unique=False)
content = Column(Text, nullable=False, unique=False)
slug = Column(String(128), nullable=False, unique=True)
categories = db.relationship('Category', secondary=posts_categories, back_populates='posts')

Sqlalchemy get data from relationship

I have this models
class Post(Base):
__tablename__ = 'posts'
id = Column(Integer, primary_key=True)
content = Column(Text)
author = Column(Integer, ForeignKey('users.id'))
to_topic = Column(Integer, ForeignKey('topics.id'))
def __init__(self, content: str, author: int, to_topic: int) -> None:
self.content = content
self.author = author
self.to_topic = to_topic
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
username = Column(String(30))
email = Column(String(40))
password = Column(String(255))
registred_at = Column(DateTime, default=datetime.datetime.now)
last_logged_in = Column(DateTime)
last_login_attempt = Column(DateTime)
avatar = Column(Integer, ForeignKey('files.id'))
role = Column(String(20))
email_confirm = Column(Boolean, default=False)
children_forum = relationship('Forum')
children_topic = relationship('Topic')
children_post = relationship('Post')
And I am trying to get query that will contain Post.content, Post.author.username but how do i do this in sessions?
I tried
posts = db_session.query(Post, User).filter(
Post.to_topic == topic_id).with_entities(Post.content, Post.author.username)
but that doesnt work because author is just integer (id) so I expect that I need somehow get the author object and post object in one query, but I dont know how. In sql that would be easy just 2 queries but here I dont know how it´s done.
query_results = db_session.query(Post, User).\
join(User, Post.author == User.id).\
filter(
Post.to_topic == topic_id
).all()
I've never used with_entities, but I know this would give you a list of tuples where query_results[0] would be your Post instance, and query_results[1] would be your User instance.
EDIT: I believe you don't have to include the Post.author == User.id bit, but it's more legible if you're explicit with your joins.

How can a check on foreign key be enforced before insert a new value in Flask SQLAlchemy?

I am building the following DB scheme in Flask-SQLAlchemy:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
app = Flask(__name__)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
posts = db.relationship('Post', backref='author', lazy='dynamic')
def __repr__(self):
return '<User {}>'.format(self.username)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
def __repr__(self):
return '<Post {}>'.format(self.body)
db.drop_all()
db.create_all()
u = User(username='susan', email='susan#example.com')
db.session.add(u)
p = Post(body='this is my first post!', user_id=1)
db.session.add(p)
# Now I create a new post for a non-existing user and I'd expect an error...
p = Post(body='this is my second post!', user_id=2)
db.session.add(p)
db.session.commit()
As you can see I manually enter a new user and then enter two posts.
I would like that on the second post insert an error is thrown because user_id=2 is not existing in the database.
But instead the insert is successful and I also see the data into the DB:
sqlite> select * from post;
1|this is my post!|2018-07-09 16:13:16.947856|1
2|this is my post!|2018-07-09 16:13:16.948996|2
sqlite>
How can I enforce the constraint in order to make it throw an error?
Thanks for your help.
To resolve this situation, instead of assigning an id to a post object, it would be better and more ORM-like to associate a User with a Post. Also, by enforcing the NOT NULLability of the foreign key column, invalid numbers cannot be added.
For example:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
app = Flask(__name__)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
posts = db.relationship('Post', lazy='dynamic', back_populates='author')
def __repr__(self):
return '<User {}>'.format(self.username)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
author_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
author = db.relationship('User')
def __repr__(self):
return '<Post {}>'.format(self.body)
db.drop_all()
db.create_all()
# Susan will be both created and added to the session
u1 = User(username='susan', email='susan#example.com')
db.session.add(u1)
# John will be created, but not added
u2 = User(username='john', email='john#example.com')
# Create a post by Susan
p1 = Post(body='this is my post!', author=u1)
# Add susan's post to the session
db.session.add(p1)
# Create a post by john, since john does not yet exist as a user, he is created automatically
p2 = Post(body='this is my post!', author=u2)
# Add john's post to the session
db.session.add(p2)
# After the session has everything defined, commit it to the database
db.session.commit()

Resources