Django 查询超过 5 小时的对象的日期时间

Posted

技术标签:

【中文标题】Django 查询超过 5 小时的对象的日期时间【英文标题】:Django query datetime for objects older than 5 hours 【发布时间】:2012-05-07 21:04:18 【问题描述】:

我正在尝试为超过 5 小时的小部件编写 Django 查询,但我有点迷茫。小部件模型有一个DateTimeField,其中填充了小部件的创建时间。

【问题讨论】:

【参考方案1】:

如果您想超过 5 小时,请使用 created__gte 而不是 created__lt

【讨论】:

这篇文章看起来像不完整的句子,为什么? “获得超过……”是什么意思?【参考方案2】:

其他答案已经涵盖了最简单的方法:只需过滤日期早于五个小时前的记录。下面是一个完整的示例,可以找到至少五秒前创建的记录:

# 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()

近年来我没有看到过这个问题,所以在大多数情况下可能不值得麻烦。

【讨论】:

【参考方案3】:

如果 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)

【讨论】:

【参考方案4】:

如果 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 来自表列并且不是硬编码值,我们将如何实现相同的目标? 这是否会过滤创建时间超过 5 小时的记录(创建时间超过 5 小时?) @GrantGubatan 不完全是:__lt 代表“小于”,因此它会过滤时间戳小于(早于)五个小时 的记录。 如果您的 DateTimeField 使用auto_now,那么它的值将由数据库服务器设置。 datetime.now() - timedelta(hours=5) 将在数据库客户端上计算,可能与数据库服务器的时间不对应。如何在数据库服务器上完全计算记录的年龄? 如果 tz 支持处于活动状态,请使用 from django.utils import timezonetimezone.now() 而不是 datetime.now()【参考方案5】:
now = datetime.datetime.now()
earlier = now - datetime.timedelta(hours=5)
MyModel.objects.filter(my_date_field__range=(earlier,now))

这应该可以解决问题。

【讨论】:

5 小时以上,不少于 5 小时! 虽然范围的东西很有用。

以上是关于Django 查询超过 5 小时的对象的日期时间的主要内容,如果未能解决你的问题,请参考以下文章

django 查询集按小时、分钟和秒排序

如何在 Swift 中从 NSDate 获取日、月和小时

PHP如何获取日期时间格式“5月19日星期二下午1:24”的小时和分钟[重复]

查询两个日期相差的月数和剩下的天数

Oracle 查询查找记录的处理时间超过 24 小时

使用 django 查询返回活动时区中的日期时间