SQLAlchemy, как установить гибридное выражение для «понимания» даты и времени?

#python #sqlite #sqlalchemy

#python #sqlite #sqlalchemy

Вопрос:

Я пытаюсь настроить гибридное свойство SQLAlchemy для декларативного отображаемого класса a Person , у которого есть вызываемое DateTime поле birth_date , представляющее дату рождения человека.

Я хочу настроить a @hybrid_property , который будет представлять возраст человека, например:

 class Person(Base):
    __tablename__ = 'person'
    name: str = Column(String)
    date_of_birth: DateTime = Column(DateTime)

    #works fine after the objects are loaded
    @hybrid_property
    def age(self):
        today = date.today()
        if self.date_of_birth:
            return today.year - self.date_of_birth.year - (
                    (today.month, today.day) < (self.date_of_birth.month, self.date_of_birth.day))
    
    @age.expression
    def age(cls):   #Don't know how to set this up
        pass
 

У меня возникли проблемы с настройкой expression для гибридного свойства. Насколько я понимаю, выражение должно возвращать оператор SQL, который помог бы фильтровать / запрашивать базу данных для возраста preson.

С этой целью работает следующий SQL

 SELECT (strftime('%Y', 'now') - strftime('%Y', person.date_of_birth)) - (strftime('%m-%d', 'now') < strftime('%m-%d', person.date_of_birth)) as age from person
 

Но я не знаю, как «указать» выражению использовать этот SQL (или даже если это правильный способ сделать это.) Я пытался использовать текст следующим образом:

 @age.expression
def age(cls):
    current_time = datetime.utcnow()   
    return text(f"{current_time.year} - strftime('%Y', '{cls.date_of_birth}')")
 

Но это не сработало. Я не знаю, как указать выражению использовать оператор SQL в качестве выбора для виртуального столбца. (Это будет столбец age)

Цель состоит в том, чтобы иметь возможность фильтровать и запрашивать age свойство следующим образом:

 session.query(Person).filter(Person.age > 25)
 

Пожалуйста, окажите помощь.

Ответ №1:

Эта часть гибридного свойства должна возвращать исполняемое предложение SQLAlchemy. И поскольку в Postgres уже есть подходящая функция для этого, вы можете просто использовать ее:

 import sqlalchemy as sa

@age.expression
def age(cls):
    return sa.func.age(cls.date_of_birth)
 

Функция: документы, ищите age(timestamp) .

Или в MySQL:

 @age.expression
def age(cls):
    return sa.func.timestampdiff('year', cls.date_of_birth, sa.func.curdate())
 

Или в SQLite:

 @age.expression
def age(cls):
  strftime = sa.func.strftime

  year_diff = strftime('%Y', 'now') - strftime('%Y', cls.date_of_birth)
  # If the month and date of the current year are before the
  # month and date of birth, then we need to subtract one year
  # because the "birthday" hasn't happened yet.
  is_partial_year = strftime('%m-%d', 'now') < strftime('%m-%d', cls.date_of_birth)

  return year_diff - is_partial_year
 

Комментарии:

1. Спасибо, что помогли мне! Не могли бы вы пояснить, что < означает оператор в этом контексте? Я имею в виду, если is_partial_year вычисляется true , то оно подстраивает is_partial_year ? Итак is_partial_year , логическое значение, которое принимает значение либо 1 или 0 , а затем происходит вычитание?

2. Да, точно, логическое значение по сути является целым числом 1/0 и обрабатывается здесь так. Это справедливо как для кода SQL, так и для кода Python