Как разделить строку в объекте набора запросов django

#python #html #django

#python #HTML #django

Вопрос:

В view.py Я пытаюсь разделить значение атрибута, которое является длинной строкой. В обычном запросе:

 someposts = Posts.object.all()[3:5]
  

должен возвращать:

 <QuerySet [object<first post>, object<second post>]>
  

Итак, затем я запрашиваю сообщения следующим образом, поскольку мне нужно разделить значение атрибута (после этого изменения я получаю ошибку):

 someposts = Posts.object.all().values('id', 'title', 'tags')[3:5]
  

таким образом, он возвращает что-то вроде:

 <QuerySet [{'id': 2, 'title': 'first post', 'tags': ' X1, X2, X3,.., X10'}, {'id': 4, 'title': 'second post', 'tags': ' S1, S2, S3,.., S8'}]
  

Но я ожидаю получить tags в виде списка жал, так что я сделал:

 splited_tags = [v['tags'].split(',') for v in someposts]
for n, i in enumerate(someposts):
        someposts[n]['tags'] = splited_tags[n]
  

и в результате

 <QuerySet [{'id': 2, 'title': 'first post', 'tags': [' X1', 'X2', 'X3',.., X10']}, {'id': 4, 'title': 'second post', 'tags': [' S1', 'S2', 'S3,.., 'S8']}]
  

поскольку я передаю someposts свой шаблон:

 context = {
       'someposts':someposts,
}
return render(request, 'app/home.html', context)
  

и в home.html:

 {%for post in someposts %}
     <a class="avator" href="{% url 'user-post' post.author.username %}"></a>
{ % endfor %}
  

Я получаю эту ошибку:

Обратный для ‘user-post’ с аргументами ‘(«,)’ не найден

Я думаю, проблема в том, что post.author.username поскольку post является строкой, у нее нет .author атрибута, поэтому он будет вычислен до string_if_invalid, который, если я не указал иначе, является пустой строкой «.

Вы знаете, как решить эту ошибку? или как разделить строку внутри набора запросов?

Ответ №1:

Конечно, у вас есть несколько вариантов, чтобы сделать это правильно, но чтобы решить вашу проблему очень быстро, я дам вам самое простое решение:

На самом деле вы можете комментировать сообщения, чтобы в каждом сообщении у вас было имя пользователя автора:

 from django.db.models import F
Post.objects.annotate(formatted_author=F('author__username')).values('id', 'formatted_author')
  

Таким образом, вы можете получить имя пользователя автора, который написал сообщение. Но будьте осторожны, чтобы, если у вас есть некоторые нулевые значения в поле author некоторых сообщений, вы не получили никаких значений в ответе.


Однако, если вы используете Postgres в качестве своей базы данных, вы можете использовать встроенную функциональность arrayField Postgres и таким образом сделать свою аннотацию еще более элегантной:

 from django.db.models.expressions import Func
from django.db.models import F, Value, TextField
from django.contrib.postgres.fields import ArrayField

formatted_author = F('author__username')
formatted_tags = Func(
    F('tags'),
    Value(","),
    function='regexp_split_to_array', output=ArrayField(TextField())
)

Post.objects 
    .annotate(formatted_author=formatted_author, formatted_tags=formatted_tags) 
    .values('id', 'formatted_author', 'formatted_tags')
  

Таким образом, вы также сэкономите затраты на выполнение SQL-запросов на каждой итерации, которые вы выполняли ранее.


Обновление 1

Если у вас нет базы данных Postgres, то мне жаль сообщать вам, что вам все еще нужно обрабатывать теги в коде python, и у меня есть несколько улучшенных рефакторингов для вашей текущей реализации. Итак, что у вас есть сейчас:

 splited_tags = [v['tags'].split(',') for v in someposts]
for n, i in enumerate(someposts):
        someposts[n]['tags'] = splited_tags[n]
  

И что я рекомендую:

 someposts = list(map(lambda somepost: dict(formatted_tags=somepost['tags'].split(','), **somepost), someposts))
  

Как вы видите, для этого требуется только одна итерация только с новым свойством компромисса, но это всего лишь мое предложение.


Обновление 2

Поскольку владелец сообщения спросил о свойствах post.author.profile.user.first_name и post.author.profile.image.url в своем комментарии, я хотел бы подробнее остановиться на этом.

В общем, вы не можете получить эти свойства, потому что вы явно не выбираете их в своей .values() функции. .values() будут получены только те свойства, которые выбраны. Итак, в основном, что вы можете сделать, это снова создать F переменные (для уменьшения длины имени) с аннотациями:

 from django.db.models import F
Post.objects.annotate(formatted_author=F('author__username'), formatted_first_name=F('author__profile__user__first_name'), formatted_image=F('author__profile__image')).values('id', 'formatted_author', 'formatted_first_name', 'formatted_image')
  

И, как подсказывает мне моя интуиция, на самом деле post.author.first_name эквивалентно post.author.profile.user.first_name , но я могу ошибаться, если у вас другая структура модели. 🙂

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

1. Спасибо за ваш ответ. Я пробовал с вашим первым решением и последним решением, поскольку я использую базу данных MySQL. В обоих случаях я получаю ошибку, в последнем говорится, что ‘d’ не определен, а в первом говорится, что существует конфликт между автором и одной из ваших форм модели, в то время как в форме модели у меня просто есть author , title , tags , date

2. Это ошибка первой части: The annotation 'author' conflicts with a field on the model.

3. Извините за опечатку, я только что проверил на своем компьютере и забыл изменить имена, теперь должны работать как 1-е, так и 3-е решения. Я забыл, что у вас уже было author свойство в Post модели, поэтому теперь я переименовал его в formatted_author , в последнем решении вместо должна была быть somepost переменная.

4. @Braiano пожалуйста, проверьте их еще раз

5. Спасибо за обновление. Я заменил его post.author.username на post.formatted_author , он почти работает, однако все еще есть два других раздела {{ post.author.profile.user.first_name }} и {{ post.author.profile.image.url }} , которые не загружают значения. Все эти переменные работали раньше!

Ответ №2:

Быстрое решение вашей проблемы:

 class Posts((models.Model):
     ....
     tags = ...
     ....
     def get_tags(self):
        if self.tags:
            return self.tags.split(",")
        else:
            None
  

а затем в вашем шаблоне попробуйте назвать это чем-то вроде:

 {% for tag in someposts.get_tags %}
      {{ tag }}
{% endfor %}
  

Однако имейте в виду, что чрезмерно толстые модели могут превратиться в кучу не поддерживаемого материала. Вы бы преобразовали некоторые свои сериализованные данные в Python, что имеет смысл сделать на уровне модели. Но попробуйте справиться с такого рода проблемами в представлении!