Фильтр Sqlalchemy по вычисленной дате и времени hybrid_property - PullRequest
2 голосов
/ 28 мая 2019

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

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)

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

Ответы [ 2 ]

2 голосов
/ 29 мая 2019

Возможно, вы пытаетесь вычислить 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)

0 голосов

В вашем @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)

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

...