Lee*_*der 7 python sqlalchemy flask flask-sqlalchemy
我正在建立一个Flask网站,其中涉及跟踪付款,我遇到了一个问题,我似乎无法按日期过滤我的一个数据库模型.
例如,如果这是我的表的样子:
payment_to, amount, due_date (a DateTime object)
company A, 3000, 7-20-2018
comapny B, 3000, 7-21-2018
company C, 3000, 8-20-2018
Run Code Online (Sandbox Code Playgroud)
我想过滤它,以便我获得7月20日之后的所有行,或者8月份的所有行等.
我可以想到一种原始的,蛮力的方式来过滤所有付款,然后迭代遍历列表按月/年过滤,但我宁愿远离这些方法.
这是我的支付数据库模型:
class Payment(db.Model, UserMixin):
id = db.Column(db.Integer, unique = True, primary_key = True)
payment_to = db.Column(db.String, nullable = False)
amount = db.Column(db.Float, nullable = False)
due_date = db.Column(db.DateTime, nullable = False, default = datetime.strftime(datetime.today(), "%b %d %Y"))
week_of = db.Column(db.String, nullable = False)
Run Code Online (Sandbox Code Playgroud)
这是我试图Payment按日期过滤:
Payment.query.filter(Payment.due_date.month == today.month, Payment.due_date.year == today.year, Payment.due_date.day >= today.day).all()
Run Code Online (Sandbox Code Playgroud)
在哪里today简单datetime.today().
我假设due_date当我调用它时,列将具有所有DateTime属性(例如.month),但似乎我错了.
Payment按日期过滤列的最佳方法是什么?谢谢您的帮助.
Chr*_*ski 16
SQLAlchemy有效地将您在Python中表达的查询转换为SQL.但它是在相对肤浅的层面上进行的,基于您Column在定义模型时分配的数据类型.
这意味着它不一定会datetime.datetime在其DateTime构造上复制Python的API - 毕竟,这两个类意味着做非常不同的事情!(datetime.datetime为Python提供日期时间功能,而SQLAlchemy DateTime告诉它的SQL转换逻辑它正在处理SQL DATETIME或TIMESTAMP列).
但别担心!你可以通过不同的方式来实现你想要做的事情,其中一些方法非常简单.我认为最简单的三个是:
datetime实例构建过滤器,而不是其组件(日,月,年).extract在过滤器中使用SQLAlchemy的构造.datetime对象这是实现您正在尝试的三种(简单)方法中最简单的方法,它也应该以最快的速度执行.基本上,不要试图在查询中单独过滤每个组件(日,月,年),只需使用单个datetime值.
基本上,以下内容应该等同于您在上面的查询中尝试执行的操作:
from datetime import datetime
todays_datetime = datetime(datetime.today().year, datetime.today().month, datetime.today().day)
payments = Payment.query.filter(Payment.due_date >= todays_datetime).all()
Run Code Online (Sandbox Code Playgroud)
现在,payments应该是在系统当前日期的开始(时间00:00:00)之后到期的所有付款.
如果您想变得更复杂,例如过去30天内过滤付款.您可以使用以下代码执行此操作:
from datetime import datetime, timedelta
filter_after = datetime.today() - timedelta(days = 30)
payments = Payment.query.filter(Payment.due_date >= filter_after).all()
Run Code Online (Sandbox Code Playgroud)
您可以使用and_和组合多个过滤器目标or_.例如,要返回在过去30天内应付款项及多是因为在15年前,你可以使用:
from datetime import datetime, timedelta
from sqlalchemy import and_
thirty_days_ago = datetime.today() - timedelta(days = 30)
fifteen_days_ago = datetime.today() - timedelta(days = 15)
# Using and_ IMPLICITLY:
payments = Payment.query.filter(Payment.due_date >= thirty_days_ago,
Payment.due_date <= fifteen_days_ago).all()
# Using and_ explicitly:
payments = Payment.query.filter(and_(Payment.due_date >= thirty_days_ago,
Payment.due_date <= fifteen_days_ago)).all()
Run Code Online (Sandbox Code Playgroud)
从您的角度来看,这里的技巧是datetime在执行查询之前正确构建过滤器目标实例.
extract构造SQLAlchemy的extract表达式(此处记录)用于执行SQL EXTRACT语句,这就是在SQL中如何从DATETIME/TIMESTAMP值中提取月,日或年.
使用这种方法,SQLAlchemy告诉您的SQL数据库"首先,从我的DATETIME列中提取月,日和年,然后过滤该提取的值".注意,这种方法会比上一个过滤较慢datetime如上所述值.但这是如何工作的:
from sqlalchemy import extract
payments = Payment.query.filter(extract('month', Payment.due_date) >= datetime.today().month,
extract('year', Payment.due_date) >= datetime.today().year,
extract('day', Payment.due_date) >= datetime.today().day).all()
Run Code Online (Sandbox Code Playgroud)
SQLAlchemy Hybrid属性是很棒的东西.它们允许您透明地应用Python功能而无需修改数据库.我怀疑这个特定的用例可能有点过分,但它们是实现你想要的第三种方式.
基本上,您可以将混合属性视为数据库中实际不存在的"虚拟列",但SQLAlchemy可以在需要时从数据库列中即时计算.
在您的具体问题,我们定义了三种混合属性:due_date_day,due_date_month,due_date_year在你的Payment模型.这是如何工作的:
... your existing import statements
from sqlalchemy import extract
from sqlalchemy.ext.hybrid import hybrid_property
class Payment(db.Model, UserMixin):
id = db.Column(db.Integer, unique = True, primary_key = True)
payment_to = db.Column(db.String, nullable = False)
amount = db.Column(db.Float, nullable = False)
due_date = db.Column(db.DateTime, nullable = False, default = datetime.strftime(datetime.today(), "%b %d %Y"))
week_of = db.Column(db.String, nullable = False)
@hybrid_property
def due_date_year(self):
return self.due_date.year
@due_date_year.expression
def due_date_year(cls):
return extract('year', cls.due_date)
@hybrid_property
def due_date_month(self):
return self.due_date.month
@due_date_month.expression
def due_date_month(cls):
return extract('month', cls.due_date)
@hybrid_property
def due_date_day(self):
return self.due_date.day
@due_date_day.expression
def due_date_day(cls):
return extract('day', cls.due_date)
payments = Payment.query.filter(Payment.due_date_year >= datetime.today().year,
Payment.due_date_month >= datetime.today().month,
Payment.due_date_day >= datetime.today().day).all()
Run Code Online (Sandbox Code Playgroud)
以下是上面的内容:
Payment模型.due_date_year,due_date_month和due_date_day.使用due_date_year作为一个例子,这是在操作的实例属性实例您的Payment类.这意味着当您执行one_of_my_payments.due_date_year该属性时将从due_datePython实例中提取值.因为这一切都发生在Python中(即不接触您的数据库),它将对datetime.datetimeSQLAlchemy已存储在您的实例中的已翻译对象进行操作.它将返回结果due_date.year.@due_date_year.expression.这个装饰器告诉SQLAlchemy,当它将引用转换due_date_year为SQL表达式时,它应该按照此方法中的定义进行转换.所以上面的例子告诉SQLAlchemy"如果你需要due_date_year在SQL表达式中使用,那么应该extract('year', Payment.due_date)如何due_date_year表达.(注意:上面的例子假设due_date_year,due_date_month和due_date_day,都是只读属性.你当然可以定义自定义setter,也可以使用@due_date_year.setter接受参数(self, value))
在这三种方法中,我认为第一种方法(过滤datetime)既易于理解,也最容易实现,并且执行速度最快.这可能是最好的方式.但这三种方法的原则非常重要,我认为这将帮助您从SQLAlchemy中获得最大价值.我希望这证明有用!
| 归档时间: |
|
| 查看次数: |
6223 次 |
| 最近记录: |