Understanding Joins in SQLAlchemy? - python

I'm having a lot of trobule understanding how joins work in SQLAlchemy, or possibly SQL altogether.. The following code works, but only because of luck and trial and error. I am not sure why it is producing the desired result.
In this case I wanted to get a list of Students for the current Instructor, all the instructor and student keys are join in a association table.. like so..
class Stud(db.Model):
__tablename__ = 'stud'
id = db.Column(db.Integer, primary_key=True, nullable=False)
first_name = db.Column(db.String(64), nullable=True)
last_name = db.Column(db.String(64), nullable=True)
email = db.Column(db.String(64), nullable=True)
cell = db.Column(db.String(10), nullable=True)
home = db.Column(db.String(10), nullable=True)
birthday = db.Column(db.DateTime(), nullable=True)
handicap = db.Column(db.String(7), nullable=True)
years_playing = db.Column(db.String(7), nullable=True)
gender = db.Column(db.String(7), nullable=True)
opt_in = db.Column(db.Boolean, nullable=True)
profile = db.Column(db.Boolean, default=False)
user_id = db.Column(db.Integer, nullable=True)
stud_inst_link = db.Table('stud_inst_link',
db.Column('inst_id', db.Integer, db.ForeignKey('inst.id')),
db.Column('stud_id', db.Integer, db.ForeignKey('stud.id')))
class Inst(db.Model):
__tablename__ = 'inst'
id = db.Column(db.Integer, primary_key=True, nullable=False)
first_name = db.Column(db.String(64), nullable=True)
last_name = db.Column(db.String(64), nullable=True)
email = db.Column(db.String(64), nullable=True)
cell = db.Column(db.String(10), nullable=True)
home = db.Column(db.String(10), nullable=True)
email_sig = db.Column(db.String, nullable=True)
birthday = db.Column(db.DateTime, nullable=True)
tax_rate = db.Column(db.Integer, nullable=True)
opt_in = db.Column(db.Boolean, nullable=True)
profile = db.Column(db.Boolean, default=False)
user_id = db.Column(db.Integer, nullable=False)
studs = db.relationship('Stud', secondary=stud_inst_link,
backref=db.backref('students', lazy='dynamic'))
The code that finally produced the result I want was this;
inst_id = 20
studs = Stud.query.join(stud_inst_link).filter(stud_inst_link.c.inst_id == inst_id).all()
for x in studs:
print(x.first_name)
It gave me the list of every student in the association table who is linked with instructor with id 20. However my program looks like it will have plenty of these joins and before continuing I would love to have a better understand of what is actually happening here.
Any help would be much appreicated.

Related

SQLAlchemy retriving a specific value and convert to an INT

I am trying to get the price value from my EVENTS class as an INT so that I can use it to make a booking by multiplying by the number of attendees for an event when I insert a new row in the booking table.
Tables in databse
class Event(db.Model):
__tablename__ = 'events'
id = db.Column(db.Integer, primary_key=True)
host = db.Column(db.String(80), nullable=False)
event_title = db.Column(db.String(80), nullable=False)
event_description = db.Column(db.String(80), nullable=False)
movie_name = db.Column(db.String(80), nullable=False)
movie_description = db.Column(db.String(80), nullable=False)
genre = db.Column(db.String(80), nullable=False)
movie_start_time = db.Column(db.Time(), nullable=False)
movie_end_time = db.Column(db.Time(), nullable=False)
classification = db.Column(db.String(80), nullable=False)
rating = db.Column(db.Integer(), nullable=False)
actors = db.Column(db.String(200), nullable=False)
directors = db.Column(db.String(200), nullable=False)
event_date = db.Column(db.Date(), nullable=False)
published_date = db.Column(db.Date(), nullable=False)
published_by = db.Column(db.String(80), nullable=False)
image = db.Column(db.String(60), nullable=False)
capacity = db.Column(db.Integer(), nullable=False)
address = db.Column(db.String(80), nullable=False)
status = db.Column(db.String(80), nullable=False)
price = db.Column(db.Integer(), nullable=False)
# ... Create the Comments db.relationship
# relation to call destination.comments and comment.destination
comments = db.relationship('Comment', backref='event')
class Booking(db.Model):
__tablename__ = 'bookings'
id = db.Column(db.Integer, primary_key=True, unique=True)
attendees = db.Column(db.Integer(), nullable=False)
total_price = db.Column(db.Integer(), nullable=False)
booked_at = db.Column(db.DateTime, default=datetime.now())
# foreign key
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
event_id = db.Column(db.Integer, db.ForeignKey('events.id'))
Code calling from Events table to get price and insert new row into Booking table
price = Event.query.with_entities(Event.price).filter_by(id = event)
user_name = User.query.with_entities(User.id).filter_by(name = current_user.name)
num_attendees = forms.attendees.data
print(num_attendees)
print(type(price))
booking = Booking(attendees=forms.attendees.data,
total_price= price * num_attendees,
user_id = user_name,
event_id = event)
#here the back-referencing works - comment.destination is set
# and the link is created
db.session.add(booking)
db.session.commit()
But I keep running into errors such as TypeError: unsupported operand type(s) for *: 'Row' and 'int'
Thank you

SQLAlchemy - get a model with all its related models that match a condition

Say I have two model like below
class User(BaseModel):
firstname = Column(String, nullable=True)
lastname = Column(String, nullable=True)
username = Column(String, unique=True, nullable=False)
password = Column(String, nullable=False)
belongings = relationship("Belonging", back_populates="user", lazy='dynamic')
class Belonging(BaseModel):
start_date = Column(DateTime, nullable=False)
end_date = Column(DateTime, nullable=True)
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
user = relationship("User", back_populates="belongings", uselist=False)
is_deleted = Column(Boolean(), default=False)
What I want to do is get the user with all the belongings which have is_deleted==False.
So each time I get a user, they only have the undeleted belongings in the list of belongings. I'd really appreciate any help for this.
Every time you get a User:
User.query.filter_by(is_deleted=False).all()

'InstrumentedList' object has no attribute 'Location' when traversing relationship

I have 3 tables that I'm having trouble traversing. These consist of a CAR table, a LOCATION table, and a CARLOCATION table. Car holds car data, location holds location data, and the carlocation table sits in the middle, and links a car to its current location.
Using Flask and SQLAlchemy, I want the user to be able to search a car by ID, which links to the CarLocation table with a relationship, and the CarLocation table links to the Location table via a relationship as well. So Car -> CarLocation -> Location
However, when I'm calling Car.CarLocation.Location.Name, I'm getting an error saying:
File "D:\Documents\GitHub\car_share\karshare\views.py", line 370, in searchcar
CurrentLocation = SearchedCar.CarLocations.Location.Name
AttributeError: 'InstrumentedList' object has no attribute 'Location'
Anybody who could assist me would be a lifesaver!
My models.py looks like below:
class Car(db.Model):
__tablename__ = 'Car'
__table_args__ = {'schema': 'KarShare'}
CarID = db.Column(db.Integer, primary_key=True, server_default=db.text(
"nextval('\"KarShare\".\"Car_CarID_seq\"'::regclass)"))
Make = db.Column(db.String(64), nullable=False)
Model = db.Column(db.String(64), nullable=False)
Year = db.Column(db.Integer, nullable=False)
Odometer = db.Column(db.Integer, nullable=False)
CarType = db.Column(db.String(64), nullable=False)
NumSeats = db.Column(db.Integer, nullable=False)
FuelType = db.Column(db.String(64), nullable=False)
CostPerHour = db.Column(db.Float, nullable=False)
TransmissionType = db.Column(db.String(9), nullable=False)
Registration = db.Column(db.String(), nullable=False)
class CarLocations(db.Model):
__tablename__ = 'CarLocations'
__table_args__ = {'schema': 'KarShare'}
CarID = db.Column(db.ForeignKey('KarShare.Car.CarID'), primary_key=True,
nullable=False)
CurrentLocationID = db.Column(db.ForeignKey('KarShare.Location.LocationID'),
nullable=True)
Location = db.relationship("Location", backref="CarLocations",
lazy=True)
Car = db.relationship("Car", backref="CarLocations",lazy=True)
class Location(db.Model):
__tablename__ = 'Location'
__table_args__ = {'schema': 'KarShare'}
LocationID = db.Column(db.Integer, primary_key=True, server_default=db.text(
"nextval('\"KarShare\".\"Location_LocationID_seq\"'::regclass)"))
Name = db.Column(db.String(64), index=True, unique=True, nullable=False)
StreetAddress = db.Column(db.String(128), nullable=False)
City = db.Column(db.String(64), index=True, nullable=False)
State = db.Column(db.String(64), index=True, nullable=False)
Longitude = db.Column(db.Numeric(precision=9, scale=6), nullable=False)
Latitude = db.Column(db.Numeric(precision=8, scale=6), nullable=False)
CarSpaces = db.Column(db.Integer, nullable=False)
And the simple code causing the error:
SearchedCar = Car.query.filter_by(CarID=form.search.data).first()
Make = SearchedCar.Make
Model = SearchedCar.Model
Year = SearchedCar.Year
Odometer = SearchedCar.Odometer
CarType = SearchedCar.CarType
NumSeats = SearchedCar.NumSeats
FuelType = SearchedCar.FuelType
CostPerHour = SearchedCar.CostPerHour
TransmissionType = SearchedCar.TransmissionType
CurrentLocation = SearchedCar.CarLocations.Location.Name
Registration = SearchedCar.Registration
Thanks for any help you can give!
Car has a list of CarLocation via relationship:
- CurrentLocation = SearchedCar.CarLocations.Location.Name
+ if SearchedCar.CarLocations:
+ CurrentLocation = SearchedCar.CarLocations[0].Location.Name

Can I use Python's Pygal module to visualize data from an SQLalchemy database?

How can I convince Pygal to pull data from 2 tables in an SQLAlchemy database?
These are my models:
class PlannedPost(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String(30), nullable=False, default='planned')
category = db.Column(db.String(30), nullable=False, default=None)
name = db.Column(db.String(30), nullable=True)
planned_amount = db.Column(db.Float, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
date_period = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
comments = db.Column(db.Text, nullable=True)
def __repr__(self):
return f"Post('{self.title}, '{self.category}'\
, '{self.name}', '{self.planned_amount}'\
, '{self.date_period}', '{self.comments}')"
class ActualPost(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String(30), nullable=False, default='actual')
category = db.Column(db.String(30), nullable=False, default=None)
actual_amount_name = db.Column(db.String(30), nullable=True)
actual_amount = db.Column(db.Float, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
comments = db.Column(db.Text, nullable=True)
def __repr__(self):
return f"ActualPost('{self.title}, '{self.category}'\
, '{self.actual_amount_name}', '{self.actual_amount}'\
, '{self.date_posted}', '{self.comments}')"
And this is what the route I dreamed up, which I know is wrong because it seems that Pygal can use only lists.
#posts.route("/graphing")
def graphing():
planned = PlannedPost.query
actual = ActualPost.query
graph = pygal.Bar()
graph.title = 'Planned Values vs Actual Values'
graph.x_labels = planned.title
graph.add('Planned', [planned.planned_amount])
graph.add('Actual', [actual.actual_amount])
graph_data = graph.render_data_uri()
return render_template('graphing.html', graph_data=graph_data)
The answer to the question is "yes". One can visualise data from a database using Pygal.
See this question for code example: Python Pygal chart pulling data from database not matching values to labels

Why am I getting InstrumentedAttribute instead of basequery

I have created two models in flask using flask_sqlalchemy as follows:
class Analytics(db.Model, IdMixin, ModelMixin):
__tablename__ = "analytics"
record_id = Column(Integer, ForeignKey("record.id"), nullable=True)
created_at = Column(TIMESTAMP, nullable=True)
updated_at = Column(TIMESTAMP, nullable=True)
class HitLog(db.Model, IdMixin, TimestampMixin, ModelMixin):
__tablename__ = "hit_logs"
record_id = Column(Integer, ForeignKey("record.id"), nullable=True)
url = Column(Text, nullable=False, default='')
ip = Column(String(100), nullable=False, default='0.0.0.0', index=True)
referer = Column(Text, nullable=False, default='')
source = Column(String(255), nullable=False, default='', index=True)
query = Column(Text, nullable=False, default='')
user_agent = Column(String(255), nullable=False, default='')
created_at = Column(TIMESTAMP, nullable=True, index=True)
updated_at = Column(TIMESTAMP, nullable=True, index=True)
deleted_at = Column(TIMESTAMP, nullable=True, index=True)
Both the models are created the same way but still when I check for type, I get different results:
print type(Analytics.query)
print type(HitLog.query)
Gives me:
<class 'flask_sqlalchemy.BaseQuery'>
<class 'sqlalchemy.orm.attributes.InstrumentedAttribute'>
Why is it so?
You have a column named query in HitLog.

Categories