Reputation: 2709
I'm trying to construct a query that filters a list of hotel rates. I only want to show hotel reservations from today and into the future. The query I built is below but does not filter the results. What am I doing wrong?
hotels = db.session.query(Hotel).\
join(Location).\
join(Rate).\
filter(Location.city == city).\
filter(Rate.arrive > datetime.utcnow())
For background, my models look like this:
class Location(Base):
__tablename__ = 'locations'
id = Column(Integer, primary_key=True)
city = Column(String(50), nullable=False, unique=True)
hotels = relationship('Hotel', back_populates='location')
class Hotel(Base):
__tablename__ = 'hotels'
id = Column(Integer, primary_key=True)
name = Column(String(100), nullable=False, unique=True)
phone_number = Column(String(20))
parking_fee = Column(String(10))
location_id = Column(Integer, ForeignKey('locations.id'), nullable=False)
location = relationship('Location', back_populates='hotels')
rates = relationship('Rate', back_populates='hotel', order_by='Rate.arrive')
class Rate(Base):
__tablename__ = 'rates'
id = Column(Integer, primary_key=True)
price = Column(Numeric(6, 2))
arrive = Column(Date, nullable=False)
link = Column(String(500), nullable=False)
updated = Column(DateTime, default=datetime.datetime.utcnow, nullable=False)
hotel_id = Column(Integer, ForeignKey('hotels.id'), nullable=False)
hotel = relationship('Hotel', back_populates='rates')
Edit: Here is some sample data:
Date: Friday, Sep 9
Rate: 299.25
Date: Sunday, Sep 11
Rate: 261.75
Date: Monday, Sep 12
Rate: 261.75
Date: Tuesday, Sep 13
Rate: 261.75
Date: Sunday, Sep 18
Removing filter(Rate.arrive > datetime.utcnow()) does not change the data. It is output the same each time I run the query.
Upvotes: 0
Views: 2214
Reputation: 76962
Option zero is to apply the filter of rates after you loaded the hotels, but this will generate additional queries, and is not desired.
Define filters which are used later on:
city = 'mumbai'
arrive = datetime.date.today()
Option-1: Load Rate
of interest in the initial query
query = (
session.query(Hotel, Rate) # this query will return pairs (Hotel, Rate)
.join(Location)
.join(Rate)
.filter(Location.city == city)
.filter(Rate.arrive > arrive)
)
# one could use the results directly as (Hotel, Rate) pairs
# but we can also convert to the format: {hotel: rates}
from collections import defaultdict
hotels_list = defaultdict(list)
for h, r in hotels:
hotels_list[h].append(r)
# and print:
for hotel, rates in hotels_list.items():
print('', hotel)
for rate in rates:
print(' ', rate)
Option-2: Trick the relationship Hotel.rates
Here we are using contains_eager
hotels = (
session.query(Hotel)
.join(Location)
.join(Rate)
.options(contains_eager(Hotel.rates)) # this is the key
.filter(Location.city == city)
.filter(Rate.arrive > arrive)
)
for hotel in hotels:
print('', hotel)
for rate in hotel.rates: # this now contains only sub-list, so be careful
print(' ', rate)
Upvotes: 1