【问题标题】:django queryset cannot run function inside annotate and Sumdjango queryset 无法在 annotate 和 Sum 中运行函数
【发布时间】:2019-12-28 11:03:38
【问题描述】:

我有两个查询集,我想从这两个查询集中进行加法,但一个查询值是 Int 类型(例如 1.5, 2.75 ),但其他查询值像字符串(例如 12:20:59)。我需要将该字符串转换为 Int 类型,以便在 Annotate 中使用 Sum 函数。

第一个查询集

timesheet_total_by_project = TimesheetEntry.objects.filter(
    created_by_id=current_user,
    timesheet__for_date__gte=start_tim,
    timesheet__for_date__lte=end_date,
    project__is_visible=True
).values(
    'project__name', 'project__id'
).distinct().annotate(totalsum=Sum('minutes', output_field=FloatField()) / 60.0)

第二个查询集

ticket_total_time = Ticket.objects.filter(
     assigned_to=user, total_time__isnull=False, project__is_visible=True
).values(
    'title','project__id'
).distinct().annotate(totalsum=Sum('total_time', output_field=FloatField()) / 60.0)

我试过了

def get_sec(time_str):
        print("string",time_str)
        """Get Seconds from time."""
        h, m, s = time_str.split(':')
        return int(h) * 3600 + int(m) * 60 + int(s)
ticket_total_time = Ticket.objects.filter(assigned_to=user,total_time__isnull=False,project__is_visible=True).values('title','project__id').distinct().annotate(totalsum=Sum(get_sec('total_time'), output_field=FloatField()) / 3600.0)

预期输出为 12.5 但输出 print("string",time_str)

string total_time

也说

ValueError: not enough values to unpack (expected 3, got 1)

【问题讨论】:

  • 这不是一件有意义的事情。一个annotate函数被翻译成SQL并应用到数据库中;你不能在那里传递任意 Python 函数。
  • @DanielRoseman 我明白这一点。但是,由于我想将字符串类型值之类的“时间”转换为整数,有什么方法可以将该值转换为整数,以便我可以在注释中执行求和?
  • 你确定这是一个字符串而不是一个时间吗?展示模型。
  • `starting_time = models.DateTimeField(null=True, blank=True) Finish_time = models.DateTimeField(null=True, blank=True) total_time = models.TextField(blank=True, null=True ) `
  • 奇怪,为什么是TextField?

标签: python django type-conversion django-queryset


【解决方案1】:
def get_sec(time_str):
    print(f'{time_str = }')
    """Get Seconds from time."""
    if not time_str:
        return 0
    h, m, s = time_str.split(':')
    return int(h) * 3600 + int(m) * 60 + int(s)

【讨论】:

  • 该功能正在运行,但是当我尝试使用 .annotate(totalsum=Sum(get_sec('total_time'), output_field=FloatField()) / 3600.0) 之类的内部注释时,它无法正常工作。那个时候它会得到确切的字符串“total_time”而不是数据库值。
  • 使用aggregate而不是annotate
【解决方案2】:

MySQL 有一个STR_TO_DATE 函数,可以将字符串转换为日期。您可以使用Func expression 运行任意数据库函数:

from django.db.models import F, Func, Value

ticket_total_time = Ticket.objects.filter(
     assigned_to=user, total_time__isnull=False, project__is_visible=True
).values(
    'title','project__id'
).distinct().
annotate(time_t=Func(F('total_time'), Value('%H:%i:%s'), function='STR_TO_DATE')).
annotate(totalsum=Sum('time_t', output_field=FloatField()) / 60.0)

【讨论】:

  • ``` class Time_to_sec(Func): function = 'TIME_TO_SEC' lookup_name = 'sec' ``` 我使用这个函数并在annotate Sum 中运行这个函数。现在我得到了秒。谢谢@Denial
  • 上述解决方案对我不起作用,但我从您的解决方案中得到了想法,我用其他方式解决了这个问题。
【解决方案3】:

我试过这个解决方案,它工作正常。现在 Time_to_sec 函数正在注释内部工作。

from django.db.models import F, Func

class Time_to_sec(Func):
           function = 'TIME_TO_SEC'
           lookup_name = 'sec'

ticket_total_time = Ticket.objects.filter(assigned_to=user,total_time__isnull=False,project__is_visible=True).values('project__id').distinct().annotate(totalsum=Sum(Time_to_sec('total_time', output_field=FloatField()) / 3600.0))

【讨论】:

    猜你喜欢
    • 2021-01-30
    • 2017-01-15
    • 2021-11-28
    • 2020-01-09
    • 2019-01-15
    • 1970-01-01
    • 1970-01-01
    • 2014-10-12
    • 2019-11-11
    相关资源
    最近更新 更多