如何在Django QuerySet对象中拆分字符串

2024-10-02 14:26:25 发布

您现在位置:Python中文网/ 问答频道 /正文

在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作为一个stings列表,所以我做了:

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 %}

我收到这个错误:

Reverse for 'user-post' with arguments '('',)' not found

我认为问题在于post.author.username,因为post是一个字符串,它没有.author属性,因此如果无效,它将被计算为字符串,除非我另外指定,否则为空字符串“”

你知道如何解决这个错误吗?或者如何在Queryset中拆分字符串


Tags: 字符串inidfor属性objecttitle错误
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,这在模型层上是有意义的。但是试着在视图中处理这些问题

当然,您有多种选择来解决问题,但为了快速解决问题,我将为您提供最简单的解决方案:

实际上,您可以为帖子添加注释,这样您就可以在每篇帖子中使用作者用户名:

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

通过这种方式,您可以获得撰写文章的作者的用户名。但是,要小心,如果您在某些帖子的作者字段中有一些null值,您将在响应中得到None


但是,如果使用Postgres作为数据库,则可以使用Postgres的内置ArrayField功能,并通过以下方式使注释更加优雅:

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_namepost.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,但是如果你有一个不同的模型结构,我可能错了

相关问题 更多 >