Python Django:是否可以转换列中的逗号分隔值并将每个值检索为查询集行

Python Django: Is it possible to convert comma separated values in a column and retrieve each value as query set rows

我在 table 中的数据集是这样的,我们将 table 命名为 plans_tracker(第一个屏幕截图)我正在尝试检索查询集,例如(第二个屏幕截图).有人可以帮我解决这个问题吗,我无法修改 table 结构。我正在尝试在 Django 模板中执行此操作

使用 PostgreSQL 时是可以的。不过,我不确定如何使用其他数据库后端来实现它。请注意,您存储数据的方式并不理想,可以使用更好的解决方案。下面有 2 个更好的解决方案示例,其中一个是独立于数据库的。

假设您有一个定义如下的模型(我已经用单个字符字段替换了示例中不相关的字段):

from django.core.validators import validate_comma_separated_integer_list
from django.db import models

class Plan(models.Model):
    group = models.CharField(max_length=256)
    student_course_records = models.TextField(validators=[validate_comma_separated_integer_list])

鉴于此,您可以使用 string_to_array 通过 Django 的 Func:

将学生列表转换为 PostgreSQL 数组
Plan.objects.annotate(
    students=Func(
        F('student_course_records'), 
        Value(','), 
        function='string_to_array',
        output_field=ArrayField(models.IntegerField())
     )
)

这将为您的对象添加一个名为 students 的“虚拟”字段。它将在一个数组中表示所有学生 ID,以便以后更容易处理。

由于您现在有一个 ID 数组,您可以使用 unnest 函数将这些值分解为单独的记录:

Plan.objects.annotate( 学生=功能( 函数( F('student_course_records'), 价值(','), 函数='string_to_array', output_field=ArrayField(models.IntegerField()) ), 函数='unnest' ) )

现在,您将拥有单独的记录,而不是 students 字段,每个记录都有一个 student 值。请注意,student_course_records 在每条记录中仍然可用,但您可以使用 valuesvalues_listonlyexclude.

更改它

为了简化这些查询的编写,您可以子类化 Func

class StringToArray(models.Func):
    function = 'string_to_array'

    def __init__(self, *args, output_field, **kwargs):
        super().__init__(*args, output_field=ArrayField(output_field), **kwargs)


class Unnest(models.Func):
    function = 'unnest'
    arity = 1

然后您的通话将如下所示:

Plan.objects.annotate(
    student=Unnest(
        StringToArray(
            F('student_course_records'), 
            Value(','), 
            output_field=models.IntegerField(),
        )
    )
)

如我之前所说,此数据模型并不理想,因为它需要在每次数据访问时解析以逗号分隔的列表。更不用说在这个字段上查询数据的麻烦了。有 2 种更好的方法:使用 ArrayField 而不是逗号分隔的文本字段或使用嵌套模型。第一种方法仍然需要 PostgreSQL 数据库,第二种方法可以与 Django 官方支持的任何数据库一起使用,但在这种简单的用例中可能显得嘈杂和多余。对于第一种方法,您可以将模型定义为:

class Plan(models.Model):
    group = models.CharField(max_length=256)
    student_course_records = ArrayField(models.IntegerField())

对于此模型,您的查询将简化为:

Plan.objects.annotate(
    student=Unnest(F('student_course_records'))
)

并且 student_course_records 在所有情况下都将表示为数组,无需将其转换为其他任何内容。

第二种方法如下:

class Plan(models.Model):
    group = models.CharField(max_length=256)

class Student(models.Model):
    plan = models.ForeignKey(Plan, related_name='student_course_records', on_delete=models.CASCADE

对于此模型,您的查询将简化为:

Student.objects.all()

使用可选的 select_relatedvalues 调用同时从 Plan 模型中提取数据。