【发布时间】:2012-05-07 21:04:18
【问题描述】:
我正在尝试为超过 5 小时的小部件编写 Django 查询,但我有点迷茫。小部件模型有一个DateTimeField,其中填充了小部件的创建时间。
【问题讨论】:
标签: django django-models django-queryset
我正在尝试为超过 5 小时的小部件编写 Django 查询,但我有点迷茫。小部件模型有一个DateTimeField,其中填充了小部件的创建时间。
【问题讨论】:
标签: django django-models django-queryset
如果您想超过 5 小时,请使用 created__gte 而不是 created__lt
【讨论】:
其他答案已经涵盖了最简单的方法:只需过滤日期早于五个小时前的记录。下面是一个完整的示例,可以找到至少五秒前创建的记录:
# Tested with Django 1.11.15 and Python 3.6.
import logging
import sys
from datetime import datetime, timedelta
from time import sleep
import django
from django.apps import apps
from django.apps.config import AppConfig
from django.conf import settings
from django.db import connections, models, DEFAULT_DB_ALIAS
from django.db.models.base import ModelBase
NAME = 'udjango'
DB_FILE = NAME + '.db'
def main():
setup()
logger = logging.getLogger(__name__)
class Widget(models.Model):
name = models.CharField(max_length=200)
date_created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
syncdb(Widget)
Widget.objects.create(name='spline')
sleep(1)
Widget.objects.create(name='reticulator')
sleep(1)
Widget.objects.create(name='tardis')
sleep(5)
Widget.objects.create(name='sonic screwdriver')
sleep(1)
cutoff_time = datetime.now() - timedelta(seconds=5)
for widget in Widget.objects.filter(date_created__lt=cutoff_time):
logger.info(widget.name)
def setup():
with open(DB_FILE, 'w'):
pass # wipe the database
settings.configure(
DEBUG=True,
DATABASES={
DEFAULT_DB_ALIAS: {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': DB_FILE}},
LOGGING={'version': 1,
'disable_existing_loggers': False,
'formatters': {
'debug': {
'format': '[%(levelname)s]'
'%(name)s.%(funcName)s(): %(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S'}},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'debug'}},
'root': {
'handlers': ['console'],
'level': 'INFO'},
'loggers': {
"django.db": {"level": "INFO"}}})
app_config = AppConfig(NAME, sys.modules['__main__'])
apps.populate([app_config])
django.setup()
original_new_func = ModelBase.__new__
@staticmethod
def patched_new(cls, name, bases, attrs):
if 'Meta' not in attrs:
class Meta:
app_label = NAME
attrs['Meta'] = Meta
return original_new_func(cls, name, bases, attrs)
ModelBase.__new__ = patched_new
def syncdb(model):
""" Standard syncdb expects models to be in reliable locations.
Based on https://github.com/django/django/blob/1.9.3
/django/core/management/commands/migrate.py#L285
"""
connection = connections[DEFAULT_DB_ALIAS]
with connection.schema_editor() as editor:
editor.create_model(model)
main()
除了最后一个小部件之外的所有内容:
[INFO]__main__.main(): spline
[INFO]__main__.main(): reticulator
[INFO]__main__.main(): tardis
这很好用,除非您启用了时区支持。在前面的示例中,我通过将 settings.configure(... 更改为如下所示:
settings.configure(
USE_TZ=True,
...
当我这样做时,我会收到这样的消息:
RuntimeWarning: DateTimeField Widget.date_created received a naive datetime (2019-01-07 16:39:04.563563) while time zone support is active.
要获得可感知时区的日期,请使用 timezone.now() function 而不是 datetime.now():
# Tested with Django 1.11.15 and Python 3.6.
import logging
import sys
from datetime import timedelta
from time import sleep
import django
from django.apps import apps
from django.apps.config import AppConfig
from django.conf import settings
from django.db import connections, models, DEFAULT_DB_ALIAS
from django.db.models.base import ModelBase
from django.utils import timezone
NAME = 'udjango'
DB_FILE = NAME + '.db'
def main():
setup()
logger = logging.getLogger(__name__)
class Widget(models.Model):
name = models.CharField(max_length=200)
date_created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
syncdb(Widget)
Widget.objects.create(name='spline')
sleep(1)
Widget.objects.create(name='reticulator')
sleep(1)
Widget.objects.create(name='tardis')
sleep(5)
Widget.objects.create(name='sonic screwdriver')
sleep(1)
cutoff_time = timezone.now() - timedelta(seconds=5)
for widget in Widget.objects.filter(date_created__lt=cutoff_time):
logger.info(widget.name)
def setup():
with open(DB_FILE, 'w'):
pass # wipe the database
settings.configure(
USE_TZ=True,
DEBUG=True,
DATABASES={
DEFAULT_DB_ALIAS: {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': DB_FILE}},
LOGGING={'version': 1,
'disable_existing_loggers': False,
'formatters': {
'debug': {
'format': '[%(levelname)s]'
'%(name)s.%(funcName)s(): %(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S'}},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'debug'}},
'root': {
'handlers': ['console'],
'level': 'INFO'},
'loggers': {
"django.db": {"level": "INFO"}}})
app_config = AppConfig(NAME, sys.modules['__main__'])
apps.populate([app_config])
django.setup()
original_new_func = ModelBase.__new__
@staticmethod
def patched_new(cls, name, bases, attrs):
if 'Meta' not in attrs:
class Meta:
app_label = NAME
attrs['Meta'] = Meta
return original_new_func(cls, name, bases, attrs)
ModelBase.__new__ = patched_new
def syncdb(model):
""" Standard syncdb expects models to be in reliable locations.
Based on https://github.com/django/django/blob/1.9.3
/django/core/management/commands/migrate.py#L285
"""
connection = connections[DEFAULT_DB_ALIAS]
with connection.schema_editor() as editor:
editor.create_model(model)
main()
有时,我会遇到数据库时钟与 Web 服务器时钟不同步的问题。为避免此类问题,您可以使用Now() function 获取数据库中的当前时间。
# Tested with Django 1.11.15 and Python 3.6.
import logging
import sys
from datetime import timedelta
from time import sleep
import django
from django.apps import apps
from django.apps.config import AppConfig
from django.conf import settings
from django.db import connections, models, DEFAULT_DB_ALIAS
from django.db.models.base import ModelBase
from django.db.models.functions import Now
from django.utils import timezone
NAME = 'udjango'
DB_FILE = NAME + '.db'
def main():
setup()
logger = logging.getLogger(__name__)
class Widget(models.Model):
name = models.CharField(max_length=200)
date_created = models.DateTimeField()
def __str__(self):
return self.name
syncdb(Widget)
Widget.objects.create(name='spline', date_created=Now())
sleep(1)
Widget.objects.create(name='reticulator', date_created=Now())
sleep(1)
Widget.objects.create(name='tardis', date_created=Now())
sleep(5)
Widget.objects.create(name='sonic screwdriver', date_created=Now())
sleep(1)
cutoff_time = Now() - timedelta(seconds=5)
for widget in Widget.objects.filter(date_created__lt=cutoff_time):
logger.info(widget.name)
def setup():
with open(DB_FILE, 'w'):
pass # wipe the database
settings.configure(
USE_TZ=True,
DEBUG=True,
DATABASES={
DEFAULT_DB_ALIAS: {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': DB_FILE}},
LOGGING={'version': 1,
'disable_existing_loggers': False,
'formatters': {
'debug': {
'format': '[%(levelname)s]'
'%(name)s.%(funcName)s(): %(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S'}},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'debug'}},
'root': {
'handlers': ['console'],
'level': 'INFO'},
'loggers': {
"django.db": {"level": "INFO"}}})
app_config = AppConfig(NAME, sys.modules['__main__'])
apps.populate([app_config])
django.setup()
original_new_func = ModelBase.__new__
@staticmethod
def patched_new(cls, name, bases, attrs):
if 'Meta' not in attrs:
class Meta:
app_label = NAME
attrs['Meta'] = Meta
return original_new_func(cls, name, bases, attrs)
ModelBase.__new__ = patched_new
def syncdb(model):
""" Standard syncdb expects models to be in reliable locations.
Based on https://github.com/django/django/blob/1.9.3
/django/core/management/commands/migrate.py#L285
"""
connection = connections[DEFAULT_DB_ALIAS]
with connection.schema_editor() as editor:
editor.create_model(model)
main()
近年来我没有看到过这个问题,所以在大多数情况下可能不值得麻烦。
【讨论】:
如果 settings.USE_TZ = True 并且 settings.TIME_ZONE 正在设置
from django.utils import timezone
five_h_ago = timezone.now()-timezone.timedelta(hours=5)
example.object.filter(datetimefield__lt=five_h_ago)
【讨论】:
如果 Widget 是您的模型的名称,并且它有一个名为 created 的 DateTimeField 属性,则查询将是:
from datetime import datetime, timedelta
time_threshold = datetime.now() - timedelta(hours=5)
results = Widget.objects.filter(created__lt=time_threshold)
注意created__lt 的意思是“创建小于”。
【讨论】:
hours 来自表列并且不是硬编码值,我们将如何实现相同的目标?
__lt 代表“小于”,因此它会过滤时间戳小于(早于)五个小时 的记录。
auto_now,那么它的值将由数据库服务器设置。 datetime.now() - timedelta(hours=5) 将在数据库客户端上计算,可能与数据库服务器的时间不对应。如何在数据库服务器上完全计算记录的年龄?
from django.utils import timezone 和 timezone.now() 而不是 datetime.now()
now = datetime.datetime.now()
earlier = now - datetime.timedelta(hours=5)
MyModel.objects.filter(my_date_field__range=(earlier,now))
这应该可以解决问题。
【讨论】: