fastAPI sqlalchemy - inner JOIN on 2 tables - python

I have a restapi up and running using the fastAPI framework, which is starting to work well.
Now: I already have my MySQL code on how to inner join on 2 tables, and I want to be able to do the same, just using sqlalchemy.
Firstly, here is my SQL code which works perfectly:
select `the_user`.`email_adress`, `the_exercise`.`exercise_name`, `run_the_workout`.`repetitions`,`run_the_workout`.`sets`,`run_the_workout`.`pause_time`,`run_the_workout`.`day_to_perform_the_task`
from `workout_plan_task` `run_the_workout`
inner join `user_profiles` `the_user` on `run_the_workout`.the_user=`the_user`.user_id
inner join `exercises` `the_exercise` on `the_exercise`.`exercise_ID` = `run_the_workout`.`the_exercise`
WHERE `run_the_workout`.the_user=1
Now, here are my table models in SQL alchemy, representing the "user_profiles", "exercises" and the "workout_plan_task" table:
#models.py:
class UserProfiles(Base):
__tablename__ = "user_profiles"
user_ID = Column(Integer, primary_key=True, index=True)
email_adress = Column(String, unique=True)
age = Column(Integer)
sex = Column(Integer)
height = Column(Integer)
weight = Column(Integer)
main_goal = Column(Integer)
level_experience = Column(Integer)
profile_created_at = Column(Date)
class Exercises(Base):
__tablename__ = "exercises"
exercise_ID = Column(Integer, primary_key=True, index=True)
exercise_name = Column(String)
exercise_type = Column(String, nullable=True)
muscle_groups_worked_out = Column(String)
equipment_ID = Column(Integer, nullable=True)
class WorkOutPlanTask(Base):
__tablename__ = "workout_plan_task"
task_ID = Column(Integer, primary_key=True, index=True)
user_ID = Column(Integer, ForeignKey("user_profiles.user_ID"))
workout_plan_ID = Column(Integer, ForeignKey("workout_plan.workout_plan_ID"))
exercise_ID = Column(Integer, ForeignKey("exercises.exercise_ID"))
repetitions = Column(Integer)
sets = Column(Integer)
pause_time = Column(Integer)
day_to_perform_the_task = Column(String)
inside my "crud.py" file, im trying to run the query using inner joins:
#crud.py
def get_workout_plan_for_user(db: Session, user_id:int):
return db.query(models.WorkOutPlanTask).join(models.UserProfiles, models.UserProfiles.user_ID == models.WorkOutPlanTask.user_ID).join(models.Exercises, models.Exercises.exercise_ID == models.WorkOutPlanTask.exercise_ID).filter(models.UserProfiles.user_ID == user_id)
and inside my #main.py i have this:
#app.get("/all_workout_plan_tasks_for_user/{user_id}")
def get_workout_plan_for_user_by_userID(user_id: int, db:Session = Depends(get_db)):
db_workout_plan = crud.get_workout_plan_for_user(db, user_id=user_id)
if db_workout_plan is None:
raise HTTPException(status_code=404, detail="sorry.. no workoutplans found ..")
return [schemas.a_workout_plan_task.from_orm(v) for v in db.query(...)]
This gives me the error:
sqlalchemy.exc.InvalidRequestError: SQL expression, column, or mapped entity expected - got 'Ellipsis'
Anyone here who could help me?

That's a funny mistake, take a look at the return statement:
return [schemas.a_workout_plan_task.from_orm(v) for v in db.query(...)]
You're passing Ellipsis to your db.query, and obviously, it's not the expected value.

Related

FastAPI sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between parent/child tables

I'm learning FastAPI and decided to write my first project. I got the error:
sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between parent/child tables on relationship ServiceVersion.servicekeyversion - there are no foreign keys linking these tables. Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression.
for:
service_model = models.Service()
models.py:
class Service(Base):
__tablename__ = 'service'
id = Column(Integer, primary_key=True, index=True)
name = Column(String, unique=True)
serviceversion = relationship("ServiceVersion", back_populates='service_v')
servicekey = relationship('ServiceKey', back_populates='service')
class ServiceVersion(Base):
__tablename__ = 'serviceversion'
id = Column(Integer, primary_key=True, index=True)
service_id = Column(Integer, ForeignKey('service.id'))
version = Column(String)
is_used = Column(Boolean)
service_v = relationship('Service', back_populates='serviceversion')
servicekeyversion = relationship('ServiceKey', back_populates='serviceversion')
class ServiceKey(Base):
__tablename__ = 'servicekey'
id = Column(Integer, primary_key=True, index=True)
service_id = Column(Integer, ForeignKey('service.id'))
version_id = Column(Integer, ForeignKey('version.id'))
service_key = Column(String)
service_value = Column(String)
service = relationship('Service', back_populates='servicekey')
serviceversion = relationship('ServiceVersion', back_populates='servicekeyversion')
schemas.py:
class CreateService(BaseModel):
name: str
version: str
is_used: bool
keys: list[Key]
class Config:
orm_mode = True
main.py:
async def post_service(service: CreateService, db: Session = Depends(get_db)):
service_model = models.Service()
I think that there is some idea i didn't catch.

How to create two tables at the same time in FAST API

i want to create two tables at the same time. and also use one tables primary key to another tables foreign key at the same time.
def createOrderPlace(request: OrderCreatePlaceOrder, db: Session):
#try:
x = format(random.randint(10000,99999), '04d')
exists_x=db.query(OrderModel).filter(OrderModel.order_id == x).first()
if exists_x:
x = format(random.randint(10000,99999), '04d')
order_create = OrderModel(
user_id=request.currentUser.user_id,
name=request.currentUser.name,
email=request.currentUser.email,
orderAmount=request.subtotal,
cvv_id=request.currentUser.cvv_id,
card_name=request.currentUser.card_name,
card_number=request.currentUser.card_number,
card_month=request.currentUser.card_month,
card_year=request.currentUser.card_year,
order_id=x,
billing_first_name=request.currentUser.billing_first_name,
billing_last_name=request.currentUser.billing_last_name,
billing_phone_number=request.currentUser.billing_phone_number,
billing_country=request.currentUser.billing_country,
billing_division=request.currentUser.billing_division,
billing_district=request.currentUser.billing_district,
billing_address=request.currentUser.billing_address,
billing_police_station=request.currentUser.billing_police_station,
billing_post_code=request.currentUser.billing_post_code,
shipping_first_name=request.currentUser.shipping_first_name,
shipping_last_name=request.currentUser.shipping_last_name,
shipping_phone_number=request.currentUser.shipping_phone_number,
shipping_country=request.currentUser.shipping_country,
shipping_division=request.currentUser.shipping_division,
shipping_district=request.currentUser.shipping_district,
shipping_address=request.currentUser.shipping_address,
shipping_police_station=request.currentUser.shipping_police_station,
shipping_post_code=request.currentUser.shipping_post_code,
)
db.add(order_create)
db_orderid = (
db.query(OrderModel)
.filter(OrderModel.user_id == request.currentUser.user_id)
.first()
)
for dic in request.cartItems:
order_item_a = OrderItemsModel(
item_id=dic.item_id,
name=dic.item_name,
price=dic.item_price,
description=dic.item_description,
user_id=dic.user_id,
payment_order_id=dic.payment_order_id,
)
db.add(order_item_a)
db.commit()
return {'invoice':request,'detail': f'Successfully Buy this Service, Check it on My service','message':True,'order_number':order_create.order_id}
i tried this but thats not work. I am using two models
class OrderModel(Base): **(model 1)**
__tablename__ = "tbl_order_and_payment-order"
id = Column(Integer, primary_key=True, index=True)
name = Column(String)
email = Column(String)
orderAmount = Column(Integer)
transactionId = Column(String)
isDelivered = Column(Boolean)
user_id = Column(Integer, ForeignKey('tbl_stu_usr-users.id'))
purchase_date = Column(DateTime, default=datetime.utcnow)
#updated_at = Column(DateTime, default=datetime.utcnow)
cvv_id=Column(String)
card_name=Column(String)
card_number=Column(String)
card_month=Column(Integer)
card_year=Column(Integer)
order_id = Column(Integer)
billing_first_name=Column(String)
billing_last_name=Column(String)
billing_phone_number=Column(String)
billing_country=Column(String)
billing_division=Column(String)
billing_district=Column(String)
billing_address=Column(String)
billing_police_station=Column(String)
billing_post_code=Column(Integer)
i have another model
class OrderItemsModel(Base):
__tablename__ = "tbl_order_and_payment-order_items"
id = Column(Integer, primary_key=True, index=True)
item_id=Column(Integer, ForeignKey('tbl_stu_adm-pricing_plan-service.id'))
user_id = Column(Integer, ForeignKey('tbl_stu_usr-users.id'))
name = Column(String)
price = Column(Integer)
description=Column(String)
product_type=Column(String)
payment_order_id=Column(Integer, ForeignKey('tbl_order_and_payment-order.id'))
i am using "tbl_order_and_payment-order.id" this primary tho this "tbl_order_and_payment-order_items" tables as payment_order_id=Column(Integer, ForeignKey('tbl_order_and_payment-order.id'))
i just want to create two tables at the same time.

SQLAlchemy query filter by field value in related table

I try to get devices which type is not 'TYPE_BLADE_SERVER'. Please help write a query. My solution which i try:
result = Device.query.filter(DeviceGroup.type != ModelType.TYPE_BLADE_SERVER).all()
Which doesnt filter device_group table becouse device_group renamed to device_group_1. Sql query from sqlalchemy:
SELECT * FROM device_group, device
LEFT OUTER JOIN device_model AS device_model_1 ON device_model_1.id = device.model_id
LEFT OUTER JOIN device_group AS device_group_1 ON device_group_1.id = device_model_1.group_id
WHERE device_group.type != % (type_1)s ; {'type_1': 'TYPE_BLADE_SERVER'}
Working solution but like sql hardcode:
result = Device.query.filter(text("device_group_1.type <> 'TYPE_BLADE_SERVER'")).all()
My models:
class Device(db.Model):
__tablename__ = 'device'
id = db.Column(db.Integer, primary_key=True)
hostname = db.Column(db.String, index=True)
model_id = db.Column(db.ForeignKey('device_model.id'), nullable=True)
model = db.relationship("DeviceModel", backref='devices', lazy='joined')
class DeviceModel(db.Model):
__tablename__ = 'device_model'
id = db.Column(db.Integer, primary_key=True)
group_id = db.Column(db.ForeignKey('device_group.id', ondelete='SET NULL'), nullable=True)
class DeviceGroup(db.Model):
__tablename__ = 'device_group'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False, unique=True)
height = db.Column(db.Integer, nullable=False)
models = db.relationship("DeviceModel", backref=backref("group", lazy="joined"), lazy='joined')
type = db.Column(sa.Enum(ModelType), nullable=False)
class ModelType(enum.Enum):
TYPE_BLADE_SERVER = 'TYPE_BLADE_SERVER'
TYPE_ENGINEERING_DEVICES = 'TYPE_ENGINEERING_DEVICES'
TYPE_DATA_STORAGE = 'TYPE_DATA_STORAGE'
You need not_
from sqlalchemy import not_
result = Device.query.filter(not_(DeviceGroup.type == ModelType.TYPE_BLADE_SERVER)).all()
or
result = Device.query.filter(~DeviceGroup.type == ModelType.TYPE_BLADE_SERVER)
Copy answer link here:
https://groups.google.com/forum/#!topic/sqlalchemy/8L1HWG7H27U|
Docs:
http://docs.sqlalchemy.org/en/latest/orm/loading_relationships.html#the-zen-of-joined-eager-loading

Modifying existing value in SQLAlchemy object

I'm trying to take an existing string in a retrieved SQLAlchemy object and concatenate it with a second string, however no value is being written.
print(messages)
testsuite = session.query(Testsuite).get(test_id)
testsuite.console += messages
session.commit()
Inspecting the database, the record has kept its original empty value - messages was never added.
My Testsuite model is as follows:
# Represents schema - used by engine to create tables etc.
Base = declarative_base()
# These SQL fields are horrendously inefficient - need replacing ASAP!
class Testsuite(Base):
"""Testsuite model to map testsuite in progress to SQL DB."""
__tablename__ = 'testsuites'
id = Column(Integer, primary_key=True)
name = Column(String)
timestamp = Column(DateTime, default=datetime.datetime.utcnow)
product_name = Column(String)
serial_number = Column(String)
total_tests = Column(Integer)
completed_tests = Column(Integer)
console = Column(Text)
report_id = Column(Integer)
testcases = relationship('Testcase', backref='testsuite')
result = Column(String)
def __init__(self, testsuite_name, product_name, serial_number, total_tests=0):
self.name = testsuite_name
self.product_name = product_name
self.serial_number = serial_number
self.total_tests = total_tests
self.completed_tests = 0
self.result = 'pending'
I've read that the way I am modifying my objects can lead to race conditions, though I am unsure of a suitable alternative. Can anyone point out the issues with what I'm doing and why my messages string isn't being added?
Thanks :)
So after a bit of experimentation, it seems that the code was failing because Testsuite.console never had an initial value.
The code now works with the following change to the mode:
class Testsuite(Base):
"""Testsuite model to map testsuite in progress to SQL DB."""
__tablename__ = 'testsuites'
id = Column(Integer, primary_key=True)
name = Column(String)
timestamp = Column(DateTime, default=datetime.datetime.utcnow)
product_name = Column(String)
serial_number = Column(String)
total_tests = Column(Integer)
completed_tests = Column(Integer, default=0)
console = Column(String, default="Waiting for incoming log data...\n")
report_id = Column(Integer)
testcases = relationship('Testcase', backref='testsuite')
result = Column(String, default='pending')
def __init__(self, testsuite_name, product_name, serial_number, total_tests=0):
self.name = testsuite_name
self.product_name = product_name
self.serial_number = serial_number
self.total_tests = total_tests

table name specified more than once

I have the following tables configured:
class User(Base):
__tablename__ = "user"
id = Column(Integer, primary_key=True)
name = Column(String)
class Gadget(Base):
__tablename__ = "gadget"
id = Column(Integer, primary_key=True)
brand = Column(String)
class UserGadget(Base):
__tablename__ = "user_gadget"
user_id = Column(Integer, ForeignKey('user.id'), primary_key=True)
gadget_id = Column(Integer, ForeignKey('gadget.id'), primary_key=True)
user = relationship("User", backref=backref('userGadgets', order_by=user_id))
gadget = relationship("Gadget", backref=backref('userGadgets', order_by=gadget_id))
class GadgetComponent(Base):
__tablename__ = "gadget_component"
id = Column(String, primary_key=True)
gadget_id = Column(Integer,ForeignKey('gadget.id'))
component_maker = Column(String)
host = relationship("Gadget", backref=backref('components', order_by=id))
class ComponentUsingMetal(Base):
__tablename__ = "component_metal"
id = Column(Integer, primary_key=True)
component_id = Column(Integer, ForeignKey('GadgetComponent.id'))
metal = Column(String)
component = relationship("GadgetComponent", backref=backref('gadgetComponentMetals', order_by=id))
On doing the following query:
session.query(User).join("userGadgets", "gadget", "components","gadgetComponentMetals").filter(ComponentUsingMetal.metal == 'iron') , component_metal gets attached to the query twice giving an error 'table name component_metal specified more than once'.
Any idea what I am doing wrong?
I traced the problem down to the following line in selectable.py:
froms = [f for f in froms if f not in toremove] This line removes the tables which are already covered by joins so that FROM clause doesn't have the same table specified more than once. The line didn't remove component_metal even though toremove had it meaning that I had two different Table objects for the same db table. And then I noticed the import for component_metal's class, ComponentUsingMetal, looked different. The others imports looked like:
from myschema import GadgetComponent
from myschema import Gadget
from python.myschema ComponentUsingMetal
As soon as fixed the import the issue went away.

Categories

Resources