【问题标题】:How to make my Django code DRY and efficient using comprehensive lists?如何使用综合列表使我的 Django 代码干燥高效?
【发布时间】:2014-05-22 11:49:36
【问题描述】:

在以下代码中,我可以看到 2 个不同的问题:

  1. 这不是 DRY,因为 date__range 中的整个逻辑对于两个列表(A 和 B)都是相同的。你会如何解决这个问题?我尝试使用方法作为参数但没有成功,不确定如何实现。

  2. get_x_events() 在每一轮循环中都被调用,我猜这效率不高..

类事件(models.Model):

    @classmethod
    def get_stats(cls, owner, nb_days, offset):

        # Building a list with comprehensive list calling the get_A_events() classmethod of the EventA class
        list_A = [EventA.get_A_events(owner).filter(
            date__range=(
                now_day - timedelta(days=offset) - timedelta(days=d),
                now_day - timedelta(days=offset) - timedelta(days=d-1))).count() \
            for d in range(nb_days)]

        # Doing the same with another method of another class
        # The logic is exactly the same thought...
        list_B = [EventB.get_B_events(owner).filter(
            date__range=(
                now_day - timedelta(days=offset) - timedelta(days=d),
                now_day - timedelta(days=offset) - timedelta(days=d-1))).count() \
            for d in range(nb_days)]

        return zip(list_A, list_B)

我的模型是这样组织的:

  • 类事件(models.Model)
  • 类 EventA(Event)
  • 类 EventB(Event)

谢谢。

【问题讨论】:

    标签: django django-models


    【解决方案1】:
    def get_events_list(event_object_with_owner, offset, nb_days):
        event_list = [ 
                       event_object_with_owner.filter(
                       date__range=(
                       now_day - timedelta(days=offset) - timedelta(days=d),
                       now_day - timedelta(days=offset) - timedelta(days=d-1))).count() \
                       for d in range(nb_days)
                      ]
        return event_list
    
    
    event_list_A = get_events_list(EventA.get_A_events(owner), offset, nb_days)
    
    event_list_B = get_events_list(EventB.get_B_events(owner), offset, nb_days)
    

    【讨论】:

    • 它有效,谢谢!小错误:调用的方法是get_events_list(不是get_events),而d不需要作为参数传递,因为它只用于综合列表的循环中。 (我编辑了你的答案)
    【解决方案2】:

    您的查询当然可以优化。现在,您为列表理解中的每个迭代执行一个 sql 查询。使用注释,您可以将其减少到 2 个查询:

    from django.db.models import Count
    
    list_A = [a['num_days'] for a in EventA.get_A_events(owner).filter(date__range=(
                 now_day - timedelta(days=offset),
                 now_day - timedelta(days=offset+nb_days))) \
                 .values('date').annotate(num_days=Count('id'))]
    

    您可以使用 **kwargs 进行过滤器调用,正如 Matt 建议的那样,但是您可以遵循 DRY 原则的程度主要取决于模型的逻辑结构,主要是它们的继承链。如果您可以发布您的模型,我可能会为您提供更多帮助。

    【讨论】:

    • 谢谢!我添加了基于模型继承的模型组织。
    【解决方案3】:

    这将是一个好的开始。

    kwargs = {
        'date__range': (now_day - timedelta(days=offset) - timedelta(days=d),
                        now_day - timedelta(days=offset) - timedelta(days=d-1)),
    }
    
    list_A = [
        EventA.get_A_events(owner).filter(**kwargs).count()
        for d in range(nb_days)
    ]
    
    list_B = [
        EventB.get_B_events(owner).filter(**kwargs).count()
        for d in range(nb_days)
    ]
    

    除此之外,您可能应该重构您的模型。拥有 EventA 和 EventB 而不是单个 Event 模型表明您进一步违反了 DRY 原则。 get_A_events 和 get_B_events 方法也是如此,为什么不使用单个 get_events 方法?

    【讨论】:

    • 请注意,d 是列表解析中的局部变量,不能在该范围之外使用。
    • 感谢您的帮助。您的解决方案不会引发任何错误,但会给我一个空查询集(而不是 3 个项目)。正如@knbk 所说,由于dvariable 我有一个警告:) 为了回答你的问题,我实际上有一个Event 类,EventA 和EventB 是inherited classes
    • 啊,在那种情况下,值得看看this。
    • 2 件事:1) 我非常需要这个工具来处理我的代码中的其他内容,所以谢谢!但是,与未来 Django 版本的兼容性如何? 2)在这个特定的问题中,我确实想拆分 A 和 B 事件以在我的图表中显示 2 行。在这种情况下,“特殊”经理会提供帮助吗?
    猜你喜欢
    • 2014-12-13
    • 2011-06-22
    • 1970-01-01
    • 2015-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-07
    • 2013-09-16
    相关资源
    最近更新 更多