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