【发布时间】:2016-11-28 07:58:13
【问题描述】:
我正在尝试在 django 1.9 中构建一个模型,该模型具有一个键、值对(字典)字段,该字段还允许查询集聚合(min、mix 等)。 我尝试使用 JSONField:
#models.py
from django.contrib.postgres import fields as pgfields
class Entry(models.Model):
pass
class Scorer(models.Model):
name = models.CharField(max_length=100)
class EntryScoreSet(models.Model):
scorer = models.ForeignKey(Scorer)
entry = models.ForeignKey(Entry, related_name="scorecard")
scores = pgfields.JSONField(default={})
....
# shell test
import random
entry = Entry()
scorer,_ = Scorer.objects.get_or_create(name="scorer1")
entry.save()
for i in range(0,10):
scores = dict(scoreA=random.random(),
scoreB=random.random(),
scoreC=random.random(),
)
entry_score_set=EntryScoreSet(scores=scores, entry=entry, scorer=scorer)
entry_score_set.save()
entry.scorecard.filter(scorer="scorer1").aggregate(Max("scores__scoreA"))
但是我遇到了来自this ticket 的错误(基本上不支持聚合)。
第二种选择是使用键值对模型(类似于this answer):
class Score(models.Model):
entry_score_set = models.ForeignKey(EntryScoreSet, db_index=True,
related_name="scores")
key = models.CharField(max_length=64, db_index=True)
value = models.FloatField(db_index=True)
但我不知道如何在查询集中获取特定键值的聚合。
如何在 Django 中实现一个键值对字段,以允许对特定键值的查询集进行聚合?
编辑:
这是一个 sn-p,它演示了我想使用 pandas 和第二个选项(键、对模型)做什么:
import django_pandas.io as djpdio
scds=Scorecard.objects.filter(
entry__in=Entry.objects.order_by('?')[:10],
scorer__name="scorer1")
scorecard_base=djpdio.read_frame(scds,fieldnames=["id","entry__id","scorer__name","scores__id"])
scores=djpdio.read_frame(Score.objects.filter(scorecard__in=scds),fieldnames=["id","key","value"])
scorecard_=(scorecard_base
.merge(scores,left_on="scores__id",right_on="id")
.pivot_table(index="entry__id",columns="key",values="value").reset_index())
scorecard=scorecard_base.merge(scorecard_,on="entry__id")
scorecard["scoreA"].max()
使用 django 的 ORM 可以实现这样的事情吗?与使用 pandas pivot 函数相比,效率如何?
【问题讨论】:
-
EntryScoreSet是否存储任何其他分数?如果没有,你可以有一个Score模型,其外键直接指向Entry,然后聚合很简单(一旦你澄清是否需要中间模型,很高兴发布一个示例)。 -
它有一个额外的外键,条目有多个评分者的分数。我会更新的。
-
是否可以将键、值对分成两个数组并执行此操作?即 ArrayField(CharField)、ArrayField(FloatField)。也许使用 F() 表达式来获取 'scoreA' 的索引