Столбец времени сортировки, полученный из значений других столбцов с использованием hybrid_property - PullRequest
1 голос
/ 31 октября 2019

Я пытаюсь создать систему бронирования событий как побочный проект для изучения Python и веб-разработки. Ниже приведены две модели, реализованные в моем проекте. EventSlot представляет временной интервал, запланированный для определенного события .

моделей

from app import db
from sqlalchemy import ForeignKey
from dateutil.parser import parse
from datetime import timedelta
from sqlalchemy.ext.hybrid import hybrid_property


class Event(db.Model):
    event_id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String, index=True, nullable=False)
    duration = db.Column(db.Float, nullable=False)
    price = db.Column(db.Float, nullable=False)

    slots = db.relationship('EventSlot', cascade='all, delete', back_populates='event')


class EventSlot(db.Model):
    slot_id = db.Column(db.Integer, primary_key=True)
    event_date = db.Column(db.DateTime, nullable=False)
    event_id = db.Column(db.Integer, ForeignKey('event.event_id'))

    event = db.relationship('Event', back_populates='slots')

Я предоставилСтраница администратора (Flask-Admin) для пользователей-администраторов для просмотра записей базы данных. На странице EventSlot я включил столбцы «Время начала» и «Время окончания», которые я хочу сделать сортируемыми. Я добавил к модели EventSlot следующее:

class EventSlot(db.Model):
    #...

    ## working as intended ##
    @hybrid_property
    def start_time(self):
        dt = parse(str(self.event_date))
        return dt.time().strftime('%I:%M %p')

    @start_time.expression
    def start_time(cls):
        return db.func.time(cls.event_date)


    ## DOES NOT WORK: can display derived time, but sorting is incorrect ##
    @hybrid_property
    def end_time(self):
        rec = Event.query.filter(Event.event_id == self.event_id).first()
        duration = rec.duration * 60
        derived_time = self.event_date + timedelta(minutes=duration)
        dt = parse(str(derived_time))
        return dt.time().strftime('%I:%M %p')

    @end_time.expression
    def end_time(cls):
        rec = Event.query.filter(Event.event_id == cls.event_id).first()
        duration = '+' + str(int(rec.duration * 60)) + ' minutes'
        return db.func.time(cls.event_date, duration)

Как видно из рисунка ниже, порядок сортировки неправильный, когда я сортирую по «времени окончания». Похоже, все еще сортировка по времени начала. В чем может быть проблема здесь?

(Правда, я все еще не понимаю hybrid_properties. Я думал, что получил его, когда заработал start_time, но теперь кажется, что я все еще ничего не понимаю ...)

sorting results

1 Ответ

1 голос
/ 31 октября 2019

В выражении для end_time cls.event_id представляет столбец, а не значение, поэтому запрос завершается неявным объединением между Event и EventSlot и выбирает первый результат этого объединения. Это, конечно, не то, что вам нужно, но вместо этого для EventSlot вы хотите узнать продолжительность соответствующего Event в SQL. Это кажется хорошим местом для использования коррелированного скалярного подзапроса:

@end_time.expression
def end_time(cls):
    # Get the duration of the related Event
    ev_duration = Event.query.\
        with_entities(Event.duration * 60).\
        filter(Event.event_id == cls.event_id).\
        as_scalar()
    # This will form a string concatenation SQL expression, binding the strings as
    # parameters to the query.
    duration = '+' + ev_duration.cast(db.String) + ' minutes'
    return db.func.time(cls.event_date, duration)

Обратите внимание, что запрос не запускается , когда к атрибуту обращаются в контексте запроса, но становится частьюродительский запрос.

...