吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 199|回复: 8
收起左侧

[求助] 求助django大佬,时间字段返回问题

[复制链接]
anning666 发表于 2024-12-19 10:42

使用django_filters库,分别过滤DateField字段和DateTimeField字段,返回的结果差异问题

  • 需求是想基于时间字段,实现按,按,按过滤数据
  • 问题点: 如果时间类型是DateField,过滤完全没有问题;如果时间类型是DateTimeField,只能按过滤,按和按过滤的结果都是空,搞不懂为什么
  • DRF 代码如下
### models
from django.db import models

class BaseModel(models.Model):
    """基类时间模型,继承专用"""
    create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
    update_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")

    class Meta:
        abstract = True 

class Departments(BaseModel):
    """
    组织架构 部门
    """
    name = models.CharField(max_length=32, unique=True, verbose_name='部门')
    pid = models.ForeignKey('self', null=True, blank=True, on_delete=models.CASCADE, verbose_name='父部门')
    demo_date = models.DateField(null=True, blank=True, verbose_name='演示日期')

### serilizers
from .models import Departments
from rest_framework import serializers

class DepartmentsModelSerializer(serializers.ModelSerializer):

    class Meta:
        model = Departments
        fields = '__all__'

### filters
from .models import Departments
from django_filters import rest_framework as filters

class DepartmentsFilter(filters.FilterSet):
    # demo_date (DateField)过滤器
    year = filters.NumberFilter(field_name='demo_date', lookup_expr='year', label='Year')
    month = filters.NumberFilter(field_name='demo_date', lookup_expr='month', label='Month')
    day = filters.NumberFilter(field_name='demo_date', lookup_expr='day', label='Day')

    # create_time (DateTimeField) 的过滤器
    create_year = filters.NumberFilter(field_name='create_time', lookup_expr='year', label='Create Year')
    create_month = filters.NumberFilter(field_name='create_time', lookup_expr='month', label='Create Month')
    create_day = filters.NumberFilter(field_name='create_time', lookup_expr='day', label='Create Day')

    class Meta:
        model = Departments
        fields = ['year', 'month', 'day', 'create_year', 'create_month', 'create_day']

### views

from .models import Departments
from .filters import DepartmentsFilter

from django_filters import rest_framework as filters
from rest_framework.viewsets import ModelViewSet

class DepartmentsModelViewSet(ModelViewSet):

    queryset = Departments.objects.all()
    ordering_fields = ('id', 'name')
    filter_backends = (filters.DjangoFilterBackend,)
    filterset_class = DepartmentsFilter
  • 然后往MySQL随便插入一点测试数据
  • 前端传参,测试效果
  • demo_date字段按,按,按,测试结果如下(正常返回过滤结果,没有问题)
- http://localhost:8000/api/departments/?year=2024
    {
    "count": 2,
    "next": null,
    "previous": null,
    "results": [
        {
            "id": 1,
            "label": "业务部",
            "pid": null,
            "create_time": "2023-05-18 09:14:40",
            "update_time": "2024-12-18T09:14:40.926475Z",
            "name": "业务部",
            "demo_date": "2024-12-18"
        },
        {
            "id": 3,
            "label": "人事部",
            "pid": null,
            "create_time": "2024-12-18 09:18:47",
            "update_time": "2024-11-18T09:18:47Z",
            "name": "人事部",
            "demo_date": "2024-11-18"
        }
    ]
}

- http://localhost:8000/api/departments/?month=12
    {
    "count": 1,
    "next": null,
    "previous": null,
    "results": [
        {
            "id": 1,
            "label": "业务部",
            "pid": null,
            "create_time": "2023-05-18 09:14:40",
            "update_time": "2024-12-18T09:14:40.926475Z",
            "name": "业务部",
            "demo_date": "2024-12-18"
        }
    ]
}

- http://localhost:8000/api/departments/?year=2023&day=9

    {
    "count": 1,
    "next": null,
    "previous": null,
    "results": [
        {
            "id": 2,
            "label": "企划部",
            "pid": null,
            "create_time": "2024-12-18 09:18:40",
            "update_time": "2024-12-18T09:18:40.693082Z",
            "name": "企划部",
            "demo_date": "2023-02-09"
        }
    ]
}
  • create_time字段按,按,按,测试结果如下(只有年份正常返回数据,月和日都是空,数据库的数据无误)
- http://localhost:8000/api/departments/?create_year=2024
    {
    "count": 2,
    "next": null,
    "previous": null,
    "results": [
        {
            "id": 2,
            "label": "企划部",
            "pid": null,
            "create_time": "2024-12-18 09:18:40",
            "update_time": "2024-12-18T09:18:40.693082Z",
            "name": "企划部",
            "demo_date": "2023-02-09"
        },
        {
            "id": 3,
            "label": "人事部",
            "pid": null,
            "create_time": "2024-12-18 09:18:47",
            "update_time": "2024-11-18T09:18:47Z",
            "name": "人事部",
            "demo_date": "2024-11-18"
        }
    ]
}

- http://localhost:8000/api/departments/?create_month=12

    {
        "count": 0,
        "next": null,
        "previous": null,
        "results": []
    }

- http://localhost:8000/api/departments/?create_day=18
    {
        "count": 0,
        "next": null,
        "previous": null,
        "results": []
    }
  • 求助大佬,为什么有这种现象?的查询结果都是空?如果想正常返回需求结果,该如何修改代码?
  • 基于DateTimeField实现按,按和按过滤数据,有什么更好的办法吗?求大佬指导,非常感谢!!!

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

ynymlpy 发表于 2024-12-19 13:27
setting.py中有时区相关的代码吗?数据库中保存的是不是utc时间
 楼主| anning666 发表于 2024-12-19 13:49
ynymlpy 发表于 2024-12-19 13:27
setting.py中有时区相关的代码吗?数据库中保存的是不是utc时间

大佬好~~~

settings配置如下

......
LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = True
Edith123 发表于 2024-12-19 15:04
这个问题的根本原因在于 DateTimeField 和 DateField 在数据库存储和查询方式上的差异。DateTimeField 存储了日期和时间,而 DateField 仅存储日期。因此,当使用 DateTimeField 进行按月或按日的过滤时,由于时间部分的存在,过滤条件可能无法正确匹配到数据,尤其是当 create_time 字段的时间部分不等于查询条件中的精确时间时。

问题原因分析:
按年查询: 由于数据库中 create_time 存储的是完整的日期和时间,只要年份匹配,数据库就会返回数据。
按月查询: create_time 字段不仅包含年份,还包含月份和时间部分。如果时间部分的分钟或秒数不同,按照 month 的查询条件就可能无法匹配。
按日查询: 同理,day 查询时,时间部分的秒、分、时等可能导致查询不匹配。

解决方案:
1. 自定义 DateTimeField 查询
要使 DateTimeField 可以按月、按日进行过滤,你可以使用 django_filters 中的 DateFromToRangeFilter 或者自定义过滤器来处理 DateTimeField 字段,使得它根据年月日来进行过滤,而忽略时间部分。

2. 修复代码:
你可以在 DepartmentsFilter 类中为 create_time 字段使用一个自定义的过滤器,忽略时间部分,只基于年月日进行过滤。

改进方案:
修改 DepartmentsFilter 类中的 create_month 和 create_day 字段的查询方式,忽略时间部分。可以通过将时间转化为零点的日期来实现。
[Python] 纯文本查看 复制代码
from django.db.models import F
from django.db.models.functions import TruncMonth, TruncDay
from django_filters import rest_framework as filters
from .models import Departments

class DepartmentsFilter(filters.FilterSet):
    # demo_date (DateField)过滤器
    year = filters.NumberFilter(field_name='demo_date', lookup_expr='year', label='Year')
    month = filters.NumberFilter(field_name='demo_date', lookup_expr='month', label='Month')
    day = filters.NumberFilter(field_name='demo_date', lookup_expr='day', label='Day')

    # create_time (DateTimeField) 的过滤器
    create_year = filters.NumberFilter(field_name='create_time', lookup_expr='year', label='Create Year')
    
    # 使用TruncMonth和TruncDay函数来忽略时间部分,基于日期的年、月和日来过滤
    create_month = filters.NumberFilter(method='filter_create_month', label='Create Month')
    create_day = filters.NumberFilter(method='filter_create_day', label='Create Day')

    class Meta:
        model = Departments
        fields = ['year', 'month', 'day', 'create_year', 'create_month', 'create_day']
    
    def filter_create_month(self, queryset, name, value):
        """按月过滤,忽略时间部分"""
        return queryset.annotate(month=TruncMonth('create_time')).filter(month__month=value)

    def filter_create_day(self, queryset, name, value):
        """按日过滤,忽略时间部分"""
        return queryset.annotate(day=TruncDay('create_time')).filter(day__day=value)

解释:
TruncMonth('create_time') 和 TruncDay('create_time') 函数是 Django 提供的数据库函数,用于截取 DateTimeField 字段的年月日部分,忽略时间(小时、分钟、秒)。
filter_create_month 和 filter_create_day 方法将处理 create_time 字段的查询,确保按月和按日过滤时,时间部分被忽略。
结果:
现在,当前端传递 create_month 或 create_day 参数时,查询会基于月份或日期进行过滤,并且忽略时间部分,返回期望的结果。
3. 使用 TruncDate 截取日期
另外,你也可以直接使用 TruncDate 来截取 DateTimeField 字段,避免复杂的自定义过滤器。例如,下面的过滤器也能处理 DateTimeField 字段:
[Python] 纯文本查看 复制代码
from django.db.models.functions import TruncDate

class DepartmentsFilter(filters.FilterSet):
    # demo_date (DateField)过滤器
    year = filters.NumberFilter(field_name='demo_date', lookup_expr='year', label='Year')
    month = filters.NumberFilter(field_name='demo_date', lookup_expr='month', label='Month')
    day = filters.NumberFilter(field_name='demo_date', lookup_expr='day', label='Day')

    # create_time (DateTimeField) 的过滤器
    create_year = filters.NumberFilter(field_name='create_time', lookup_expr='year', label='Create Year')
    create_month = filters.NumberFilter(field_name='create_time', lookup_expr='month', label='Create Month')
    create_day = filters.NumberFilter(field_name='create_time', lookup_expr='day', label='Create Day')

    class Meta:
        model = Departments
        fields = ['year', 'month', 'day', 'create_year', 'create_month', 'create_day']

    def filter_create_month(self, queryset, name, value):
        """按月过滤,忽略时间部分"""
        return queryset.annotate(date=TruncDate('create_time')).filter(date__month=value)

    def filter_create_day(self, queryset, name, value):
        """按日过滤,忽略时间部分"""
        return queryset.annotate(date=TruncDate('create_time')).filter(date__day=value)

结论:
通过 TruncMonth, TruncDay 或 TruncDate,你可以忽略时间部分,仅依据年月日进行过滤,从而解决 DateTimeField 字段按月和按日过滤无结果的问题。
Lyss07 发表于 2024-12-19 17:20
Edith123 发表于 2024-12-19 15:04
这个问题的根本原因在于 DateTimeField 和 DateField 在数据库存储和查询方式上的差异。DateTimeField 存储 ...

我学习下
baliao 发表于 2024-12-19 21:51
Edith123 发表于 2024-12-19 15:04
这个问题的根本原因在于 DateTimeField 和 DateField 在数据库存储和查询方式上的差异。DateTimeField 存储 ...

感谢大佬分享! 这么详细 不是AI 作答吧?
 楼主| anning666 发表于 2024-12-20 09:13
Edith123 发表于 2024-12-19 15:04
这个问题的根本原因在于 DateTimeField 和 DateField 在数据库存储和查询方式上的差异。DateTimeField 存储 ...

修改以后,依然一样的问题
类似?create_year=2024的过滤查询依然有效
但类似?create_month=12的查询依然没有过滤出来...

  • debug调试的时候,后端有收到值,但感觉去db查询的时候,过滤不出来值....
 楼主| anning666 发表于 2024-12-20 09:21
  • 如果一个模型里面,既定义DateTimeField字段,又定义DateField字段,我感觉这样不合理...因为DateTimeField已经包含了DateField
  • 如果基于DateField,需求已经实现了,但现在使用DateTimeField过滤就是有问题...
  • 求有经验大佬指导一下,这样的需求该怎么做....
Edith123 发表于 2024-12-24 15:56
anning666 发表于 2024-12-20 09:13
[md]修改以后,依然一样的问题
类似`?create_year=2024`的过滤查询依然有效
但类似`?create_month=12`的 ...

https://chatgpt.com/share/676a6909-acf0-8004-93f4-0ec0ffe52ff2
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-1-2 20:08

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表