如何将非数据库字段添加到数据库驱动的 Django 模型,并通过保留其数据类型的 QuerySet 设置它?

发布于 2024-09-17 11:55:28 字数 1271 浏览 12 评论 0原文

我有一个包含各种数据库模型字段的 Django 模型。 管理器的查询集之一通过一些注释调用检索各种聚合。其中一些注释是定制的,并从数据库中检索浮点值。这些注释不是模型字段的一部分。 但是,当创建查询集时,这些浮点数在模型中变成了整数,我猜是因为没有模型字段将它们绑定到浮点数或小数数据类型。

下面是一些代码来演示我的意思:

自定义聚合类。请注意,数据库将结果转换为浮点数:

class SqlCTR(aggregates.Sum):
    is_ordinal = True
    sql_function = 'SUM' 
    sql_template= "CASE WHEN sum(campaignmanager_adstats.impressions) > 0 THEN sum(campaignmanager_adstats.clicks)/sum(campaignmanager_adstats.impressions)::float ELSE 0::float END"

class CTR(Sum):
    name='CTR'
    def add_to_query(self, query, alias, col, source, is_summary):
        aggregate = SqlCTR(col, source=source, is_summary=is_summary)
        query.aggregates[alias] = aggregate

这是查询集:

camps =  self.select_related(depth=3).\      
  annotate( impressions=Sum('ad__adstats__impressions'),\      
  clicks=Sum('ad__adstats__clicks'),\      
  ctr=CTR('ad__adstats__clicks'),\      
  exclude(**campaignExclude).\      
  filter(**campaignArgs).order_by(sortBy)

问题是,虽然查询本身运行正常并以浮点数形式返回 CTR,但将其作为浮点数排序并过滤它就好了(如果我在 Postgres 中运行生成的 sql)控制台),生成的查询集将值转换为整数,导致 0... (请记住,CTR 不是模型字段)。

我如何确保带注释的值以正确的数据类型加载到模型中?我可以设置 DecimalField 或 FloatField 的非数据库模型字段来保留类型吗?

任何想法都将受到高度赞赏!
谢谢
哈雷尔

I've got a Django model containing various database model fields.
One of the manager's querysets retrieves various aggregations via some annotate calls. Some of those annotations are custom made and retrieve float values from the database. Those annotations are not part of the model's fields.
However, when the queryset is created, those floats turn out to be integers in the model, I guess because the there's not a model field to bound them to a float or decimal data type.

Here's some code to demostrate what I mean:

The custom aggregate classes. Note the database casts the result to a float:

class SqlCTR(aggregates.Sum):
    is_ordinal = True
    sql_function = 'SUM' 
    sql_template= "CASE WHEN sum(campaignmanager_adstats.impressions) > 0 THEN sum(campaignmanager_adstats.clicks)/sum(campaignmanager_adstats.impressions)::float ELSE 0::float END"

class CTR(Sum):
    name='CTR'
    def add_to_query(self, query, alias, col, source, is_summary):
        aggregate = SqlCTR(col, source=source, is_summary=is_summary)
        query.aggregates[alias] = aggregate

And here's the queryset:

camps =  self.select_related(depth=3).\      
  annotate( impressions=Sum('ad__adstats__impressions'),\      
  clicks=Sum('ad__adstats__clicks'),\      
  ctr=CTR('ad__adstats__clicks'),\      
  exclude(**campaignExclude).\      
  filter(**campaignArgs).order_by(sortBy)

The problem is that although the query itself runs ok and returns CTR as floats, sorts it as a float and filters it just fine (if I run the generated sql in Postgres's console), the resulting Queryset translates the value to an integer, resulting in 0s...
(Remember CTR is not a model field).

How can I either make sure that the annotated values load in their right datatype to the model? Can I set a non database model field of DecimalField or FloatField which will preserve the type?

Any idea will be highly appreciated!
Thanks
Harel

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

檐上三寸雪 2024-09-24 11:55:32

此代码表示在尝试下面 Omer 的建议后 django shell 的输出...似乎无法在注释中编写任何代码...

>>> usr = User.objects.get(username='harel')  
>>> camps = Campaign.objects.campaigns(usr, {'ctr__gt':0, 'sort':'ctr','dir':'DESC'})  
>>> for c in camps:  
...     print "%s:: %d/%d=%d  (ctr type is %s)" % (c, c.clicks, c.impressions, c.ctr, str(type(c.ctr)))  
.    ..   
Dan Schedule Test:: 10/15135=0  (ctr type is <type 'int'>)  
DTR-04-06:: 35/101827=0  (ctr type is <type 'int'>)  
US-Advertising-ad4:: 1/2991=0  (ctr type is <type 'int'>)  
Harels Test New Camp:: 51/153929=0  (ctr type is <type 'int'>)  
Commercial Team:: 161/512072=0  (ctr type is <type 'int'>)  
US-Marketing-ad3:: 1/3405=0  (ctr type is <type 'int'>)  

This code denomstrates output from the django shell after trying Omer's proposal below... Can't seem to write any code in the comments...

>>> usr = User.objects.get(username='harel')  
>>> camps = Campaign.objects.campaigns(usr, {'ctr__gt':0, 'sort':'ctr','dir':'DESC'})  
>>> for c in camps:  
...     print "%s:: %d/%d=%d  (ctr type is %s)" % (c, c.clicks, c.impressions, c.ctr, str(type(c.ctr)))  
.    ..   
Dan Schedule Test:: 10/15135=0  (ctr type is <type 'int'>)  
DTR-04-06:: 35/101827=0  (ctr type is <type 'int'>)  
US-Advertising-ad4:: 1/2991=0  (ctr type is <type 'int'>)  
Harels Test New Camp:: 51/153929=0  (ctr type is <type 'int'>)  
Commercial Team:: 161/512072=0  (ctr type is <type 'int'>)  
US-Marketing-ad3:: 1/3405=0  (ctr type is <type 'int'>)  
ˇ宁静的妩媚 2024-09-24 11:55:32

我来回答我自己的问题:
事实证明(显然),浏览 django 代码本身有很大帮助。
我的 SqlCTR 调用有: is_ordinal = True django 代码中的注释状态:

is_ordinal, a boolean indicating if the output of this aggregate
           is an integer (e.g., a count)

而我需要 is_computer=True

is_computed, a boolean indicating if this output of this aggregate
           is a computed float (e.g., an average), regardless of the input
           type.

I'll answer my own question:
Turns out (obviously), that going through the django code itself is a lot of help.
My SqlCTR call had: is_ordinal = True which the comments in the django code state:

is_ordinal, a boolean indicating if the output of this aggregate
           is an integer (e.g., a count)

while I needed is_computer=True

is_computed, a boolean indicating if this output of this aggregate
           is a computed float (e.g., an average), regardless of the input
           type.
陌伤ぢ 2024-09-24 11:55:31

根据django代码中的文档:

source 是底层字段或聚合定义
列参考。如果聚合不是序数或
计算类型,此引用用于确定强制
聚合的输出类型。

因此,尝试将字段类型放在SqlCTR的构造函数中的source中。

aggregate = SqlCTR(col, source=source, is_summary=is_summary)

应该是:

aggregate = SqlCTR(col, source=models.DecimalField(), is_summary=is_summary)

According to the documentation in the django code:

source is the underlying field or aggregate definition for
the column reference. If the aggregate is not an ordinal or
computed type, this reference is used to determine the coerced
output type of the aggregate.

So, try putting the field type in source in the constructor of SqlCTR.

aggregate = SqlCTR(col, source=source, is_summary=is_summary)

should be:

aggregate = SqlCTR(col, source=models.DecimalField(), is_summary=is_summary)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文