Python Django: Можно ли конвертировать значения, разделенные запятыми, в столбец и извлекать каждое значение в виде строк набора запросов

#python #django #templates #orm #comma

Вопрос:

Мой набор данных в таблице выглядит так, давайте назовем имя таблицы как plans_tracker (1-й снимок экрана) Я пытаюсь получить набор запросов, например (2-й снимок экрана). может ли кто-нибудь, пожалуйста, помочь мне в этом, я не смог изменить структуру таблицы. Я пытаюсь сделать это в шаблонах Django введите описание изображения здесь

введите описание изображения здесь

Ответ №1:

Это возможно при использовании 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])
 

Учитывая это, вы можете преобразовать список студентов в массив PostgreSQL string_to_array с помощью Django Func :

 Plan.objects.annotate(
    students=Func(
        F('student_course_records'), 
        Value(','), 
        function='string_to_array',
        output_field=ArrayField(models.IntegerField())
     )
)
 

Это добавит «виртуальное» поле к вашему объекту с именем students . Он будет представлять все идентификаторы учащихся в массиве, который будет легче обработать позже.

Поскольку теперь у вас есть массив идентификаторов, вы можете разбить значения на отдельные записи, используя unnest функцию:

Планируйте.объекты.аннотируйте( студент=Функция( Функция( F(‘student_course_records’), Значение(‘,’), функция=’string_to_array’, поле вывода=Поле массива(модели.Целочисленное поле()) ), функция=’unnest’ ) )

Теперь вместо students поля у вас будут отдельные записи, каждая с одним student значением. Обратите внимание, что student_course_records это по-прежнему доступно в каждой записи, но вы можете изменить его с помощью values , values_list , only или exclude .

Чтобы упростить написание этих запросов, вы можете подкласс 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 во всех случаях он будет представлен в виде массива, без необходимости преобразовывать его во что-либо еще.

2-й подход будет выглядеть так:

 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_related или values вызовами для одновременного извлечения данных из Plan модели.

Комментарии:

1. Большое вам спасибо @GwynBleidD к сожалению, мы используем базу данных mysql, и данные таблицы уже доступны. Мне просто нравится неинтересная функция postgres. работаю над ручным разделением строки на массив и пытаюсь добавить ее в набор запросов. пожалуйста, помогите мне, если есть какая-либо библиотека, доступная для выполнения этой функции в mysql.

2. Вы также можете попробовать использовать JSONField, он недавно был перенесен во все базы данных. К сожалению, он имеет ограниченную возможность запрашивать данные в базах данных, не поддерживающих его изначально.