这是我正在使用的Django模型的简化版本。
class Course(models.Model):
infos = JSONField()
信息JSONField看起来像这样:
infos = {
category: "Maths",
students: [
{
name: "Alice",
result: 8
},
{
name: "Bob",
result: 12
}
]
}
# (students can have 0 -> n items)
我正在尝试获取所有课程中至少10名学生的列表(结果> = 10)。 但是我在寻找一种方法来相应地过滤QuerySet时遇到麻烦。
我正在尝试做这样的事情:
(Course.objects.filter(students__result__gte=10)
.values_list('students', flat=True))
但是由于学生是一个列表,所以我无法直接访问每个项目的result属性。此外,我认为它不会从结果中排除“ Alice”对象。
我想得到这样的结果:
items = [
{
name: "Bob",
result: 12
}
]
奖励积分,如果有一种方法可以将过滤的学生与课程类别链接起来:
items = [
{
category: "Maths",
students: [
{
name: "Bob",
result: 12
}
]
}
]
我怎么能达到预期的效果?
答案 0 :(得分:2)
通过使用原始SQL,我设法获得了结果。
select array_to_json(array_agg(students_array))
FROM
course_table,
json_array_elements(CAST(course_table.infos->>'students' as json)) students_array
WHERE
CAST(students_array->>'result' as integer) >= 10
;
sample_db=# SELECT version();
PostgreSQL 10.12 (Ubuntu 10.12-0ubuntu0.18.04.1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0, 64-bit
sample_db=# select infos from course_table;
{"category": "Maths", "students": [{"name": "Alice", "result": 8}, {"name": "Bob", "result": 12}]}
{"category": "Science", "students": [{"name": "Jerin", "result": 8}, {"name": "George", "result": 12}]}
{"category": "Physics", "students": [{"name": "Vivek", "result": 17}, {"name": "Osama", "result": 6}]}
sample_db=# select array_to_json(array_agg(students_array))
sample_db-# FROM
sample_db-# course_table,
sample_db-# json_array_elements(CAST(course_table.infos->>'students' as json)) students_array
sample_db-# WHERE
sample_db-# CAST(students_array->>'result' as integer) >= 10
sample_db-# ;
[{"name": "Bob", "result": 12},{"name": "George", "result": 12},{"name": "Vivek", "result": 17}]
此原始SQL可以由Django执行,
raw_sql = """
select 1 as id, array_to_json(array_agg(students_array)) as result
FROM
course_table,
json_array_elements(CAST(course_table.infos->>'students' as json)) students_array
WHERE
CAST(students_array->>'result' as integer) >= 10
;
"""
qs = Course.objects.raw(raw_sql)
for i in qs:
print(i.result)
答案 1 :(得分:2)
如果您使用的是PostgreSQL数据库,则可以切换到HStoreField([此处为文档] [1])。 HStoreField将允许您完全使用您提到的查找(students__result__gte
)。否则,最像Django的方式将是在问题下方的注释中创建像Arakkal建议的Student模型。
编辑:鉴于所有的地图/字典都是相同的(如果您确实希望与JSON具有相同的结构),这是能够进行过滤的最简单方法:
class Course(models.Model):
pass
class Info(models.Model):
course = models.ForeignKey(Course, related_name='infos')
category = models.CharField(max_length=128)
# students = models.HStoreField() # available in postgresql
class Student(models.Model):
"""If not using PostgreSQL, also more Django-esque."""
info = models.ForeignKey(Info, related_name='students')
name = models.CharField(max_length=128)
result = models.IntegerField()
我正在尝试获取所有课程中至少获得10名学生的列表(结果> = 10)
鉴于您的目标和我对逻辑关系的直觉,这将是我偏爱的结构:
class Course(models.Model):
students = models.ManyToManyField(Student, through=Result)
category = models.CharField(max_length=128)
class Student(models.Model):
name = models.CharField(max_length=128)
class Result(models.Model):
course = models.ForeignKey(Course, on_delete=models.deletion.CASCADE)
student = models.ForeignKey(Student, on_delete=models.deletion.CASCADE)
grade = models.IntegerField()
这允许:Result.objects.filter(grade__gte=10).values_list('student__name', flat=True)
答案 2 :(得分:0)
您可以尝试为课程课程定义custom manager。这将使您能够使用Django-ORM样式进行所需的查询,而无需使用原始SQL。
PS:-如果适合您,我将用更多代码和详细信息编辑此答案。