Django:属性和queryset具有完全相同角色的干代码?

2024-04-24 09:32:26 发布

您现在位置:Python中文网/ 问答频道 /正文

在我的Django代码中,对于OrderedArticle对象,我需要计算一个hist_price,它是两个字段的乘积:hist_unit_price*quantity。你知道吗

我做这件事的第一个方法是一个简单的属性:

class OrderedArticle(Model):
    @property
    def hist_price(self):
        return self.hist_unit_price * self.quantity

然后,我意识到当我需要对这些价格进行大量计算时,出于性能原因,我不能使用这个属性,而是必须在数据库级别计算hist_price。这就是为什么我为此编写了一个自定义查询集:

class OrderOperationQuerySet(Queryset):

    @staticmethod
    def _hist_price(orderable_field):  # can be an OrderedArticle or another object here
        return ExpressionWrapper(
            F(f'{orderable_field}__hist_unit_price') * F(f'{orderable_field}__quantity'),
            output_field=DecimalField())

目前,我的代码中同时使用了hist_price属性和_hist_price查询集。你知道吗

问题

这很管用,但我很恼火写两次相同的业务逻辑。我有种感觉我在这里做得不对。 我认为我应该在代码级别确保,无论我使用属性还是queryset,它总是返回相同的结果。 在这个特定的例子中,业务逻辑是两个小数之间的简单乘法,所以应该可以,但是我的代码中还有其他更复杂的情况。你知道吗

你有办法改进我的代码吗?谢谢。你知道吗


Tags: 代码selffieldreturn属性defunit业务
1条回答
网友
1楼 · 发布于 2024-04-24 09:32:26

这个想法类似于"hybrid attributes" from SQLAlchemy,它一直是asked about before——我对来自线程链的任何答案都不太满意(比如将这个计算值存储在表上的一个额外字段中,并且总是确保它保持更新)。你知道吗

只要所需的运算符重载为接受实际值或F()对象(例如基本数学运算符),您的属性和ExpressionWrapper函数都可以使用一些内部函数。你知道吗

def multiplication(x, y):
    return x * y  # trivial here but it could be any mathematical expression really


def _hist_price(orderable_field):
    return ExpressionWrapper(
        multiplication(
            F(f"{orderable_field}__hist_unit_price"),
            F(f"{orderable_field}__quantity")
        ),
        output_field=DecimalField()
    )

@property
def hist_price(self):
    return multiplication(self.hist_unit_price, self.quantity)

如果在其中一个混合函数中,它比基本的数字操作更复杂,并且您希望避免重复的业务逻辑,那么您需要编写一个包装函数,它可以使用python函数为属性调用者解析到正确的输出,以及可以为查询集调用者操作F对象以保持运算符重载的函数。但这将导致代码内省参数,以确定要做什么,这可能是不直观的,所以这是一种权衡。你知道吗

在粗糙的伪代码中,其中一个自定义函数如下

def _hybrid_lower(value):
   if isinstance(value, F):  # maybe F would be sufficient or some other class higher in the hierarchy
       # https://docs.djangoproject.com/en/2.2/ref/models/expressions/#func-expressions
       return Func(value, function='LOWER')
   else:
       return value.lower()

然后可以在属性和queryset都调用的函数中使用这个自定义函数。如果您确实开始需要非常复杂的函数,例如数据库操作和Python,那么代码的一些重复可能不是最糟糕的权衡。你知道吗

相关问题 更多 >