Запрос Django для объединения нескольких полей массива в одну текстовую строку

#python #django #postgresql #django-orm

Вопрос:

У меня есть объектная модель, в которой Documents есть длинные текстовые файлы, которые могут иметь Attachments , и оба набора объектов также могут быть похожи на электронные таблицы Tables . Каждая таблица содержит прямоугольный массив с текстом. Я хочу, чтобы пользователи могли искать ключевое слово по содержимому таблицы, но результаты будут отображаться в основном документе (поэтому вместо просмотра каждой соответствующей таблицы вы просто увидите документ, в котором больше всего таблиц соответствует вашему запросу).

Ниже вы можете увидеть тестовый запрос, который я пытаюсь выполнить, который в идеальном мире преобразует все содержимое таблицы (во всех вложениях) в одну длинную строку, которую я затем могу передать в поисковый индикатор, чтобы сделать заголовок. По какой-то причине тестовый запрос возвращает таблицы как разные объекты, а не объединенные в одну длинную строку. Я использую пользовательскую функцию, которая имитирует Postgres 13 StringAgg , как я использую Postgres 10.

Заранее спасибо за вашу помощь, дайте мне знать, если мне понадобится предоставить дополнительную информацию, чтобы повторить это.

мой models.py :

 class Document(AbstractDocument):
  tables = GenericRelation(Table)

class Attachment(AbstractDocument):
  tables_new = GenericRelation(Table)  
  main_document = ForeignKey(Document, on_delete=CASCADE, related_name="attachments")

class Table(models.Model):
  content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
  object_id = models.SlugField()
  content_object = GenericForeignKey()

  content = ArrayField(ArrayField(models.TextField(null=True)))
 

мой вопрос:

 def myStringAgg(field: str):
  return Func(
    F(field),
    Value(" "),
    Value(""),
    function="array_to_string",
    output_field=models.TextField(),
  )


s = Document.objects.all() 
  .annotate(tt=myStringAgg("attachments__tables__content")) 
  .values_list('tt', flat=True)
# what I get
>>> <DocumentSet ['table1', 'table2']>
# what I want
>>> <DocumentSet ['table1 table2']>
 

Я использую Django 3.2 и. Postgres 10

Чтобы уточнить, какова моя полная область, вот как будет выглядеть окончательный запрос:

 qs = Document.objects.filter(  
  Q(tables__search_vector=query) | 
  Q(attachments__tables__search_vector=query)
  )
  .annotate(rank=rank)
  .order_by("-rank")
  .annotate(snippet=SearchHeadline(
     myStringAgg("attachments__tables__content"),
   query, max_fragments=5)
)
 

Ответ №1:

 You can use the join function to create a string from a list:
s = Document.objects.all() 
  .annotate(tt=myStringAgg("attachments__tables__content")) 
  .values_list('tt', flat=True)

s = " ". join(list(s))
 

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

1. Это не сработает, так как я передаю полученную строку для дальнейшей обработки SQL (переходя к вызову функции SearchHeadline). Поэтому мне нужно, чтобы соединение произошло в SQL-запросе, а не после.