Фильтр Sqlalchemy по вычисляемому гибридному_свойству даты и времени

У меня есть рецепт модели.

from datetime import timedelta
from sqlalchemy.ext.hybrid import hybrid_property


class Prescription(db.Model):
    """ docstring """
    ID = db.column(db.Integer, primary_key=True)
    date = db.Column(db.DateTime)
    duration = db.Column(db.SmallInteger)

    @hybrid_property
    def expiration_date(self):
        # return self.date + timedelta(days=self.duration)
        return self.date + timedelta(days=7)

    @classmethod
    def actual(cls ):
        """ docstring """
        today = datetime.today()

        return cls.query.filter(Prescription.expiration_date>=today)

Я хочу получать только актуальные рецепты в моем фактическом методе, и когда я указываю

@hybrid_property
def expiration_date(self): 
    return self.date + timedelta(days=7)

все работает как шарм.

Но каждый рецепт имеет разную продолжительность, и когда я указываю

@hybrid_property
def expiration_date(self): 
     return self.date + timedelta(days=self.duration)

у меня ошибка

TypeError: unsupported type for timedelta days component: InstrumentedAttribute

Я попытался сделать небольшой хак, как это

@property
def days(self):
    return int(self.duration)

но не повезло. Может ли кто-нибудь сказать какой-нибудь обходной путь или создать какой-то ленивый объект для поля продолжительности или, может быть, другим способом получить фактические рецепты, фильтруя по рассчитанному expire_date?

какую базу данных вы используете?

Tech at The Sparks Foundation 28.05.2019 18:03

PostgreSQL и пакет psycopg2-binary==2.7.7

Sergey Miletskiy 28.05.2019 18:04
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
2
628
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

В вашем @classmethod аннотированном actual() методе вы пытаетесь получить доступ к нестатическому свойству (expiration_date), которое вызывает ошибку. Насколько я понимаю, что делает ваш код, я изменил код следующим образом:

from datetime import datetime,timedelta
from sqlalchemy.ext.hybrid import hybrid_property
from flask_sqlalchemy import SQLAlchemy
from flask import Flask

app = Flask(__name__)
db = SQLAlchemy(app)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.config['SQLALCHEMY_DATABASE_URI'] ='sqlite:///'

class Prescription(db.Model):
    """ docstring """
    def mydefault(context):
        return context.get_current_parameters()['date'] + timedelta(days=context.get_current_parameters()['duration'])

    ID = db.Column(db.Integer,primary_key= True)
    date = db.Column(db.Date)
    duration = db.Column(db.SmallInteger)
    expiration_date = db.Column(db.Date,default = mydefault)

    @classmethod
    def actual(cls ):
        """ docstring """
        today = datetime.today().date()
        return cls.query.filter(Prescription.expiration_date>=today).all()
    def __repr__(self):
      return "Id:{} , date:{}, duration:{}, expiration_date:{}".format(self.ID,self.date,self.duration,self.expiration_date)

db.create_all()
q=Prescription(ID=1,date=datetime(2019,5,26).date(),duration = 1) #Expired
r=Prescription(ID=2,date=datetime(2019,5,20).date(),duration = 3) #Expired
s=Prescription(ID=3,date=datetime(2019,5,27).date(),duration = 5) #Not Expired
t = Prescription(ID=4,date=datetime.now().date(),duration = 1) #Not Expired

db.session.add(q)
db.session.add(r)
db.session.add(s)
db.session.add(t)
db.session.commit()

list_obj = Prescription.query.all()
print("All Objects in DB:-")
for l in list_obj:
  print(l)
print()
print("Valid Prescription:")
print(Prescription.actual())

Выход:

Output

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

date = db.Column(db.DateTime)

к

date = db.Column(db.Date)

и это облегчит вашу задачу :)

пожалуйста, попробуйте сделать запрос Prescription.actual(), потому что после запроса я получаю сообщение об ошибке.

Sergey Miletskiy 28.05.2019 18:35

в этом случае actual является методом класса. если вы хотите сравнить атрибуты чего-то в нем. вам также нужно передать объект. не так ли?

Tech at The Sparks Foundation 28.05.2019 18:59

да, это метод класса, но мне нужно вычислить expire_date для каждого объекта, поэтому сравнение атрибутов запускается при вызове запроса.

Sergey Miletskiy 28.05.2019 19:03

можете ли вы добавить какой-нибудь код, где вы используете, чтобы я ясно понял

Tech at The Sparks Foundation 28.05.2019 19:07

просто создайте несколько объектов рецептов с разными датами и временем и попытайтесь получить их. Например. Prescription(ID=1,date=datetime(2019,5,26),duration = 1) Prescription(ID=2,date=datetime(2019,5,20),duration = 3) Prescription(ID=3,date=datetime(2019,5,27),duration = 5) Prescription.actual()

Sergey Miletskiy 28.05.2019 19:18

Когда вызывается Prescription.actual(). В своем коде он использует Prescription.expiration_date, который зависит от объекта, поэтому он принимает self в качестве аргумента. Как вы будете удовлетворять его зависимость.

Tech at The Sparks Foundation 28.05.2019 19:23

упс, ты прав. Я думаю, что решением в моем случае может быть добавление дополнительного столбца expiration_date, а затем запрос по нему. Большое спасибо.

Sergey Miletskiy 28.05.2019 19:28

Конечно, нет проблем. Всего наилучшего :)

Tech at The Sparks Foundation 28.05.2019 19:37

Определенно да.

Sergey Miletskiy 28.05.2019 19:40

Можете ли вы проголосовать и принять ответ, если это было полезно. Так что это помогает всем, кто посещает ваш вопрос. Хорошего дня.

Tech at The Sparks Foundation 01.06.2019 16:29
Ответ принят как подходящий

Возможно, вы пытаетесь вычислить DATEDIFF: Вычислить DATEDIFF в POSTGRES, используя SQLAlchemy

Здесь is_expired сгенерирует часть запроса SQL, которая вычисляет разницу в днях между датой начала и utcnow() и сравнивает результат с self.duration

Это работает в PostgreSQL, но я не тестировал другие СУБД.

from datetime import datetime
import sqlalchemy as sa


class Prescription:
    ID = db.column(db.Integer, primary_key=True)
    date = db.Column(db.DateTime)
    duration = db.Column(db.SmallInteger)

    @hybrid_property
    def is_expired(self):
        days_since_published = sa.func.trunc((
            sa.extract('epoch', datetime.utcnow()) -
            sa.extract('epoch', self.date)
        ) / 3600 / 24)

        return days_since_published >= self.duration

    @classmethod
    def active(cls):
        return cls.query.filter(is_expired=False)

Другие вопросы по теме